private async Task <bool> StartConnecting() { return(await Task <bool> .Factory.StartNew(() => { try { NodeConnectionParameters parameters = new NodeConnectionParameters(); //So we find nodes faster parameters.TemplateBehaviors.Add(new AddressManagerBehavior(GetAddressManager())); //So we don't have to load the chain each time we start parameters.TemplateBehaviors.Add(new ChainBehavior(GetChain())); //Tracker knows which scriptPubKey and outpoints to track parameters.TemplateBehaviors.Add(new TrackerBehavior(GetTracker())); var nodeReq = new NodeRequirement() { RequiredServices = NodeServices.Network }; _group = new NodesGroup(_wallet.NetworkChoice, parameters, nodeReq); _group.MaximumNodeConnection = MAX_NUM_CONNECTIONS; _group.Connect(); _connectionparameters = parameters; return true; } catch (Exception e) { throw e; } })); }
private async void StartConnecting() { await Task.Factory.StartNew(() => { NodeConnectionParameters parameters = new NodeConnectionParameters(); //So we find nodes faster parameters.TemplateBehaviors.Add(new AddressManagerBehavior(this.GetAddressManager())); //So we don't have to load the chain each time we start parameters.TemplateBehaviors.Add(new ChainBehavior(this.GetChain())); //Tracker knows which scriptPubKey and outpoints to track parameters.TemplateBehaviors.Add(new TrackerBehavior(this.GetTracker())); var nodeReq = new NodeRequirement() { RequiredServices = NodeServices.Network }; _group = new NodesGroup(_wallet.NetworkChoice, parameters, nodeReq); _group.MaximumNodeConnection = MAX_NUM_CONNECTIONS; _group.Connect(); _connectionparameters = parameters; }); _wallet.Configure(_group); _wallet.Connect(); _wallet.Connected = true; PeriodicKick(); PeriodicSave(); }
public void CanMaintainConnectionToNodes() { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { servers.Server1.InboundNodeConnectionParameters.Services = NodeServices.Network; AddressManagerBehavior behavior = new AddressManagerBehavior(new AddressManager()); behavior.AddressManager.Add(new NetworkAddress(servers.Server1.ExternalEndpoint), IPAddress.Parse("127.0.0.1")); NodeConnectionParameters parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(behavior); NodesGroup connected = new NodesGroup(Network.TestNet, parameters, new NodeRequirement() { RequiredServices = NodeServices.Network }); connected.AllowSameGroup = true; connected.MaximumNodeConnection = 2; connected.Connect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Server crash abruptly servers.Server1.ConnectedNodes.First().Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Client crash abruptly connected.ConnectedNodes.First().Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); } }
public void CanMaintainConnectionToNodes() { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { NodesGroup connected = CreateGroup(servers, 2); connected.Connect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Server crash abruptly servers.ConnectedNodes.First().Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Client crash abruptly connected.ConnectedNodes.First().Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); connected.Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 0); } }
public void CanMaintainConnectionToNodes() { using (var builder = NodeBuilder.Create()) { NodesGroup connected = CreateGroup(builder, 2); connected.Connect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Server crash abruptly builder.Nodes[0].Kill(false); TestUtils.Eventually(() => connected.ConnectedNodes.Count < 2); builder.Nodes[0].Start(); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); //Client crash abruptly connected.ConnectedNodes.First().Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); //Reconnect ? TestUtils.Eventually(() => connected.ConnectedNodes.Count == 2); connected.Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 0); } }
public async Task Connect() { await Task.Factory.StartNew(async() => { EndpointInstance = await BusManager.StartServiceBus(Network.Name); _nodeGroup = new NodesGroup(Network, GetNodeConnectionParameters()) { AllowSameGroup = true, }; _nodeGroup.Requirements.SupportSPV = true; foreach (NetworkAddress networkAddress in BootstrapNodes) { AddressManager.Add(networkAddress); } _nodeGroup.Connect(); _nodeGroup.ConnectedNodes.Added += (s, e) => { var node = e.Node; node.MessageReceived += RebroadcastEvent; node.MessageReceived += (node1, message) => { Console.WriteLine("Message Received"); SaveChainData(); }; node.Disconnected += n => { Console.WriteLine("Disconnected!"); }; }; }); }
NodesGroup CreateNodeGroup(ConcurrentChain chain, int startHeight) { AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(NodeEndpoint), IPAddress.Loopback); NodesGroup group = new NodesGroup(Network, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new ExplorerBehavior(this, chain) { StartHeight = startHeight }, new ChainBehavior(chain) { CanRespondToGetHeaders = false } } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; group.Connect(); return(group); }
private async void StartConnecting() { await Task.Factory.StartNew(() => { var parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(GetAddressManager())); //So we find nodes faster parameters.TemplateBehaviors.Add(new ChainBehavior(GetChain())); //So we don't have to load the chain each time we start parameters.TemplateBehaviors.Add(new TrackerBehavior(GetTracker())); //Tracker knows which scriptPubKey and outpoints to track, it monitors all your wallets at the same if (!_Disposed) { _Group = new NodesGroup(App.Network, parameters, new NodeRequirement() { RequiredServices = NodeServices.Network //Needed for SPV }); _Group.MaximumNodeConnection = 4; _Group.Connect(); _ConnectionParameters = parameters; } }); PeriodicSave(); PeriodicUiUpdate(); PeriodicKick(); foreach (var wallet in Wallets) { wallet.Wallet.Configure(_Group); wallet.Wallet.Connect(); } }
private async Task LoadGroup() { AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(_ChainConfiguration.NodeEndpoint), IPAddress.Loopback); NodesGroup group = new NodesGroup(_Network.NBitcoinNetwork, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new ExplorerBehavior(_Repository, _Chain, _AddressPoolService, _EventAggregator) { StartHeight = _ChainConfiguration.StartHeight }, new SlimChainBehavior(_Chain), new PingPongBehavior() } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; var task = WaitConnected(group); group.Connect(); try { await task; } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failure to connect to the bitcoin node (P2P)"); throw; } _Group = group; group.ConnectedNodes.Added += ConnectedNodes_Changed; group.ConnectedNodes.Removed += ConnectedNodes_Changed; // !Hack. ExplorerBehavior.AttachCore is async and calling the repository. // Because the repository is single thread, calling a ping make sure that AttachCore // has fully ran. // Without this hack, NBXplorer takes sometimes 1 min to go from Synching to Ready state await _Repository.Ping(); }
private async Task LoadGroup() { AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(GetEndpoint()), IPAddress.Loopback); NodesGroup group = new NodesGroup(_Network.NBitcoinNetwork, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new ExplorerBehavior(_Repository, _Chain, _EventAggregator) { StartHeight = _Configuration.ChainConfigurations.First(c => c.CryptoCode == _Network.CryptoCode).StartHeight }, new ChainBehavior(_Chain) { CanRespondToGetHeaders = false, SkipPoWCheck = true, StripHeader = true }, new PingPongBehavior() } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; var task = WaitConnected(group); group.Connect(); try { await task; } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failure to connect to the bitcoin node (P2P)"); throw; } _Group = group; group.ConnectedNodes.Added += ConnectedNodes_Changed; group.ConnectedNodes.Removed += ConnectedNodes_Changed; }
private async Task StartConnecting() { await Task.Factory.StartNew(() => { var parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(GetAddressManager())); parameters.TemplateBehaviors.Add(new ChainBehavior(GetChain())); parameters.TemplateBehaviors.Add(new TrackerBehavior(GetTracker())); if (!_disposed) { _group = new NodesGroup(_settings.Network, parameters, new NodeRequirement() { RequiredServices = NodeServices.Network }); _group.MaximumNodeConnection = 4; _group.Connect(); _connectionParameters = parameters; } }); // PeriodicSave(); _logger.Information("Configuring groups"); _wallet.Configure(_group); if (_isNewWallet) { _logger.Information("New wallet, generating key pool "); //_wallet.GetNextScriptPubKey(); GenerateNewAddress(); SaveWallet(); } _logger.Information("Connecting Wallet"); PeriodicKick(); _wallet.NewWalletTransaction += _wallet_NewWalletTransaction; _wallet.Connect(); _logger.Information("Wallet connected"); _lastHeight = CurrentHeight; //while (!_disposed) //{ // await Task.Delay(10000); // _logger.Information($"Current Height {CurrentHeight}"); // _logger.Information($"Connected Nodes {ConnectedNodes.Count}"); // SaveAsync(); //} }
/// <summary> /// Starts and manages a connection to multiple peers in the Bitcoin network. /// </summary> public void Connect() { var parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(GetAddressManager())); parameters.TemplateBehaviors.Add(new ChainBehavior(GetChain())); _peers = new NodesGroup(Network.Main, parameters, new NodeRequirement() { RequiredServices = NodeServices.Network }); _peers.MaximumNodeConnection = 8; _peers.Connect(); _connectionParameters = parameters; PeriodicSave(); PeriodicReconnect(); PeriodicUpdate(); }
private void StartCore() { LogMessageContext.Create("Starting..."); using (var Servers = new TesterNodeServerSet(3)) { Node node1 = Handshake(Servers [0], Servers [1], "node1"); Node node2 = Handshake(Servers [1], Servers [2], "node2"); NBitcoin.Protocol.AddressManager addressManager = new NBitcoin.Protocol.AddressManager(); addressManager.PeersToFind = 4; NodeConnectionParameters parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); // Servers.SeedServerIndex = 2; //TODO NodesGroup nodesGroup = new NodesGroup(JsonLoader <Network> .Instance.Value, parameters); nodesGroup.AllowSameGroup = true; //TODO nodesGroup.MaximumNodeConnection = 2; //TODO nodesGroup.Connect(); Thread.Sleep(20000); //TODO Console.ForegroundColor = ConsoleColor.Blue; LogMessageContext.Create("Found nodes: " + nodesGroup.ConnectedNodes.Count); foreach (Node node in nodesGroup.ConnectedNodes) { LogMessageContext.Create("Found node: " + node.RemoteSocketAddress + " " + node.RemoteSocketPort); } LogMessageContext.Create("Stopping"); } }
public async Task InitializeConnection(Wallet currentWallet) { await Task.Factory.StartNew(() => { var parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManagerService.LoadAddressManager())); parameters.TemplateBehaviors.Add(new ChainBehavior(chainService.LoadChain().Result)); parameters.TemplateBehaviors.Add(new TrackerBehavior(trackerService.LoadTracker().Result)); var group = new NodesGroup(Constants.CurrentNetwork, parameters, new NodeRequirement { RequiredServices = NodeServices.Network }) { MaximumNodeConnection = 5 }; group.Connect(); currentWallet.Configure(group); currentWallet.Connect(); }); }
public void ShouldSyncBlockChainAgainstLocal() { var network = new TestNetwork(); network.AddSeed(new NetworkAddress(new IPEndPoint(IPAddress.Parse("192.168.2.101"), 9999))); var p = new TestTransactionPool(); p.Add("t1", 1); p.Add("t2", 0); p.Spend("t2", "t1", 0); p.Render(); var genesisBlock = new TestBlock(p.TakeOut("t1").Value); var block1 = new TestBlock(p.TakeOut("t2").Value); block1.Parent = genesisBlock; genesisBlock.Render(); block1.Render(); WithBlockChains(1, genesisBlock.Value.Key, blockChains => { // blockChains[0].HandleNewBlock(genesisBlock.Value.Value); // blockChains[0].HandleNewBlock(block1.Value.Value); AutoResetEvent waitForConnection = new AutoResetEvent(false); bool connected = false; blockChains[0].OnAddedToStore += transaction => { Trace.Information("-- Transaction Received (node server)"); // actionReceiver(); }; AddressManager addressManager = new AddressManager(); addressManager.PeersToFind = 1; NodeConnectionParameters nodesGroupParameters = new NodeConnectionParameters(); // nodesGroupParameters.AddressFrom = servers[1].ExternalEndpoint; nodesGroupParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); nodesGroupParameters.TemplateBehaviors.Add(new ChainBehavior(blockChains[0])); nodesGroupParameters.TemplateBehaviors.Add(new BroadcastHubBehavior()); nodesGroupParameters.TemplateBehaviors.Add(new SPVBehavior(blockChains[0], BroadcastHub.GetBroadcastHub(nodesGroupParameters.TemplateBehaviors))); NodesGroup nodesGroup = new NodesGroup(network, nodesGroupParameters); nodesGroup.AllowSameGroup = true; nodesGroup.MaximumNodeConnection = 1; nodesGroup.ConnectedNodes.Added += (object sender, NodeEventArgs e) => { Trace.Information("-- Node added to node group"); connected = true; waitForConnection.Set(); }; nodesGroup.Connect(); Assert.True(waitForConnection.WaitOne(10000)); //TODO: use reset events instead of sleep Assert.True(connected); //TODO Thread.Sleep(40000); // actionSender(BroadcastHub.GetBroadcastHub(nodesGroup.NodeConnectionParameters)); Trace.Information("-- Done"); }); }
private void BroadcastTransaction(Action <BroadcastHub> actionSender, Action actionReceiver) { WithServerSet(2, servers => { WithBlockChains(2, null, blockChains => { AutoResetEvent waitForConnection = new AutoResetEvent(false); bool connected = false; servers.SeedServerIndex = 0; AddressManager serverAddressManager = new AddressManager(); serverAddressManager.Add( new NetworkAddress(servers[0].ExternalEndpoint), servers[0].ExternalEndpoint.Address ); serverAddressManager.Connected(new NetworkAddress(servers[0].ExternalEndpoint)); NodeConnectionParameters serverParameters = new NodeConnectionParameters(); serverParameters.TemplateBehaviors.Add(new AddressManagerBehavior(serverAddressManager)); serverParameters.TemplateBehaviors.Add(new BroadcastHubBehavior()); serverParameters.TemplateBehaviors.Add(new SPVBehavior(blockChains[0], BroadcastHub.GetBroadcastHub(serverParameters.TemplateBehaviors))); blockChains[0].OnAddedToMempool += transaction => { Trace.Information("-- Transaction Received (node server)"); actionReceiver(); }; servers[0].InboundNodeConnectionParameters = serverParameters; #region NodeGroup AddressManager addressManager = new AddressManager(); addressManager.PeersToFind = 1; NodeConnectionParameters nodesGroupParameters = new NodeConnectionParameters(); nodesGroupParameters.AddressFrom = servers[1].ExternalEndpoint; nodesGroupParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); nodesGroupParameters.TemplateBehaviors.Add(new BroadcastHubBehavior()); nodesGroupParameters.TemplateBehaviors.Add(new SPVBehavior(blockChains[1], BroadcastHub.GetBroadcastHub(nodesGroupParameters.TemplateBehaviors))); blockChains[1].OnAddedToMempool += transaction => { Trace.Information("-- Transaction Received (node group)"); }; NodesGroup nodesGroup = new NodesGroup(servers.Network, nodesGroupParameters); nodesGroup.AllowSameGroup = true; nodesGroup.MaximumNodeConnection = 1; nodesGroup.ConnectedNodes.Added += (object sender, NodeEventArgs e) => { Trace.Information("-- Node added to node group"); connected = true; waitForConnection.Set(); }; nodesGroup.Connect(); #endregion Assert.True(waitForConnection.WaitOne(10000)); //TODO: use reset events instead of sleep Assert.True(connected); actionSender(BroadcastHub.GetBroadcastHub(nodesGroup.NodeConnectionParameters)); Trace.Information("-- Done"); }); }); }
public void CanHandshakeWithSeveralTemplateBehaviors() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); node.Generate(101); AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(node.NodeEndpoint), IPAddress.Loopback); var chain = new SlimChain(builder.Network.GenesisHash); NodesGroup group = new NodesGroup(builder.Network, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new SlimChainBehavior(chain), new PingPongBehavior() } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; var connecting = WaitConnected(group); try { group.Connect(); connecting.GetAwaiter().GetResult(); Eventually(() => { Assert.Equal(101, chain.Height); }); var ms = new MemoryStream(); chain.Save(ms); var chain2 = new SlimChain(chain.Genesis); ms.Position = 0; chain2.Load(ms); Assert.Equal(chain.Tip, chain2.Tip); using (var fs = new FileStream("test.slim.dat", FileMode.Create, FileAccess.Write, FileShare.None, 1024 * 1024)) { chain.Save(fs); fs.Flush(); } chain.ResetToGenesis(); using (var fs = new FileStream("test.slim.dat", FileMode.Open, FileAccess.Read, FileShare.None, 1024 * 1024)) { chain.Load(fs); } Assert.Equal(101, chain2.Height); chain.ResetToGenesis(); } finally { group.Disconnect(); } } }
public void CanHandshakeRestrictNodes() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); var manager = new AddressManager(); manager.Add(new NetworkAddress(node.NodeEndpoint), IPAddress.Loopback); var nodesRequirement = new NodeRequirement() { MinStartHeight = 100 }; var nodeConnectionParameters = new NodeConnectionParameters() { TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None } } }; var group = new NodesGroup(builder.Network, nodeConnectionParameters, nodesRequirement); group.AllowSameGroup = true; var connecting = WaitConnected(group); try { group.Connect(); connecting.GetAwaiter().GetResult(); } catch (TaskCanceledException) { // It is expected because no node should connect. Assert.Empty(group.ConnectedNodes); // but we chack it anyway. } finally { group.Disconnect(); } node.Generate(101); group = new NodesGroup(builder.Network, nodeConnectionParameters, nodesRequirement); group.AllowSameGroup = true; connecting = WaitConnected(group); try { group.Connect(); connecting.GetAwaiter().GetResult(); Eventually(() => { Assert.NotEmpty(group.ConnectedNodes); Assert.All(group.ConnectedNodes, connectedNode => Assert.True(connectedNode.RemoteSocketEndpoint.IsEqualTo(node.NodeEndpoint))); }); } finally { group.Disconnect(); } } }
public async Task TestServicesAsync(string networkString) { await RuntimeParams.LoadAsync(); var network = Network.GetNetwork(networkString); var blocksToDownload = new List <uint256>(); if (network == Network.Main) { blocksToDownload.Add(new uint256("00000000000000000037c2de35bd85f3e57f14ddd741ce6cee5b28e51473d5d0")); blocksToDownload.Add(new uint256("000000000000000000115315a43cb0cdfc4ea54a0e92bed127f4e395e718d8f9")); blocksToDownload.Add(new uint256("00000000000000000011b5b042ad0522b69aae36f7de796f563c895714bbd629")); } else if (network == Network.TestNet) { blocksToDownload.Add(new uint256("0000000097a664c4084b49faa6fd4417055cb8e5aac480abc31ddc57a8208524")); blocksToDownload.Add(new uint256("000000009ed5b82259ecd2aa4cd1f119db8da7a70e7ea78d9c9f603e01f93bcc")); blocksToDownload.Add(new uint256("00000000e6da8c2da304e9f5ad99c079df2c3803b49efded3061ecaf206ddc66")); } else { throw new NotSupportedNetworkException(network); } var dataDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName()); BitcoinStore bitcoinStore = new BitcoinStore(); await bitcoinStore.InitializeAsync(Path.Combine(dataDir, EnvironmentHelpers.GetMethodName()), network); var addressManagerFolderPath = Path.Combine(dataDir, "AddressManager"); var addressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{network}.dat"); var blocksFolderPath = Path.Combine(dataDir, "Blocks", network.ToString()); var connectionParameters = new NodeConnectionParameters(); AddressManager addressManager = null; try { addressManager = await NBitcoinHelpers.LoadAddressManagerFromPeerFileAsync(addressManagerFilePath); Logger.LogInfo($"Loaded {nameof(AddressManager)} from `{addressManagerFilePath}`."); } catch (DirectoryNotFoundException) { addressManager = new AddressManager(); } catch (FileNotFoundException) { addressManager = new AddressManager(); } catch (OverflowException) { File.Delete(addressManagerFilePath); addressManager = new AddressManager(); } catch (FormatException) { File.Delete(addressManagerFilePath); addressManager = new AddressManager(); } connectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); connectionParameters.TemplateBehaviors.Add(bitcoinStore.CreateUntrustedP2pBehavior()); using var nodes = new NodesGroup(network, connectionParameters, requirements: Constants.NodeRequirements); KeyManager keyManager = KeyManager.CreateNew(out _, "password"); WasabiSynchronizer syncer = new WasabiSynchronizer(network, bitcoinStore, new Uri("http://localhost:12345"), Global.Instance.TorSocks5Endpoint); ServiceConfiguration serviceConfig = new ServiceConfiguration(50, 2, 21, 50, new IPEndPoint(IPAddress.Loopback, network.DefaultPort), Money.Coins(Constants.DefaultDustThreshold)); CachedBlockProvider blockProvider = new CachedBlockProvider( new P2pBlockProvider(nodes, null, syncer, serviceConfig, network), new FileSystemBlockRepository(blocksFolderPath, network)); using Wallet wallet = Wallet.CreateAndRegisterServices( network, bitcoinStore, keyManager, syncer, nodes, dataDir, new ServiceConfiguration(50, 2, 21, 50, new IPEndPoint(IPAddress.Loopback, network.DefaultPort), Money.Coins(Constants.DefaultDustThreshold)), syncer, blockProvider); Assert.True(Directory.Exists(blocksFolderPath)); try { var mempoolTransactionAwaiter = new EventsAwaiter <SmartTransaction>( h => bitcoinStore.MempoolService.TransactionReceived += h, h => bitcoinStore.MempoolService.TransactionReceived -= h, 3); var nodeConnectionAwaiter = new EventsAwaiter <NodeEventArgs>( h => nodes.ConnectedNodes.Added += h, h => nodes.ConnectedNodes.Added -= h, 3); nodes.Connect(); var downloadTasks = new List <Task <Block> >(); using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(4)); foreach (var hash in blocksToDownload) { downloadTasks.Add(blockProvider.GetBlockAsync(hash, cts.Token)); } await nodeConnectionAwaiter.WaitAsync(TimeSpan.FromMinutes(3)); var i = 0; var hashArray = blocksToDownload.ToArray(); foreach (var block in await Task.WhenAll(downloadTasks)) { Assert.True(File.Exists(Path.Combine(blocksFolderPath, hashArray[i].ToString()))); i++; } await mempoolTransactionAwaiter.WaitAsync(TimeSpan.FromMinutes(1)); } finally { // So next test will download the block. foreach (var hash in blocksToDownload) { await blockProvider.BlockRepository.RemoveAsync(hash, CancellationToken.None); } if (wallet is { })
private void DumpCoins(BitcoinAddress destination, IEnumerable <Coin> coins, IEnumerable <Key> keys) { var total = coins.Select(u => u.Amount).Sum(); var fee = Money.Zero; FeeRate feeRate = null; try { feeRate = RPCClient.EstimateFeeRate(6); //BCC having few fees, this will confirm fast } catch { if (RPCClient.Network == Network.RegTest) { feeRate = new FeeRate(Money.Satoshis(30), 1); } else { Logs.Main.LogWarning("Fee estimation unavailable, you need to wait Bitcoin Core to properly sync and retry to dump"); return; } } TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(coins); builder.Send(destination, total); try { builder.SendEstimatedFees(feeRate); builder.BuildTransaction(false); } catch (NotEnoughFundsException ex) { fee = (Money)ex.Missing; } builder = new TransactionBuilder(); builder.AddKeys(keys.ToArray()); builder.AddCoins(coins); builder.Send(destination, total - fee); builder.SendFees(fee); var dumpTransaction = builder.BuildTransaction(true, SigHash.ForkId | SigHash.All); Logs.Main.LogInformation("Dump transaction created " + dumpTransaction.ToHex()); Logs.Main.LogInformation("Dump transaction ID " + dumpTransaction.GetHash()); //repository.lock(coins.select(c => c.outpoint).toarray(), dumptransaction.gethash()); Thread.Sleep(1000); while (true) { Console.WriteLine("Are you sure to dump " + coins.Select(c => c.Amount).Sum().ToString() + " BCash coin to " + destination.ToString() + " ? (type `yes` to continue)"); var response = Console.ReadLine(); if (response.Equals("yes", StringComparison.OrdinalIgnoreCase)) { break; } } Logs.Main.LogInformation("Connecting to a BCC node..."); AddressManagerBehavior addrman = new AddressManagerBehavior(new AddressManager()); addrman.PeersToDiscover = 10; addrman.Mode = AddressManagerBehaviorMode.Discover; NodesGroup group = new NodesGroup(RPCClient.Network, new NodeConnectionParameters() { IsRelay = false, Services = NodeServices.Nothing | NodeServices.NODE_BITCOIN_CASH, TemplateBehaviors = { addrman }, UserAgent = "BCCSpliter", Advertize = false }, new NodeRequirement() { RequiredServices = NodeServices.NODE_BITCOIN_CASH }); group.MaximumNodeConnection = 1; if (Configuration.BCCEndpoint != null) { addrman.PeersToDiscover = 1; addrman.Mode = AddressManagerBehaviorMode.None; addrman.AddressManager.Add(new NetworkAddress(Configuration.BCCEndpoint), IPAddress.Parse("127.0.0.1")); group.CustomGroupSelector = WellKnownGroupSelectors.ByEndpoint; group.AllowSameGroup = true; } group.ConnectedNodes.Added += (s, e) => { Logs.Main.LogInformation("Connected to " + e.Node.RemoteSocketEndpoint); Logs.Main.LogInformation("Broadcasting..."); e.Node.SendMessageAsync(new InvPayload(new InventoryVector(InventoryType.MSG_TX, dumpTransaction.GetHash()))); e.Node.SendMessageAsync(new TxPayload(dumpTransaction)); CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(10000); try { e.Node.PingPong(cts.Token); } catch (Exception ex) { Logs.Main.LogWarning("Error while broadcasting transaction, retrying with another node..."); e.Node.Disconnect("Error while broadcasting transaction", ex); return; } Logs.Main.LogInformation("Broadcasted " + dumpTransaction.GetHash()); group.Disconnect(); group.Dispose(); }; group.Connect(); }
public async Task Connect(NetworkInfo networkInfo) { IPAddress ipAddress = null; WireSerialization.Instance.Magic = networkInfo.Magic; NodeServerTrace.Information($"Magic is {networkInfo.Magic}"); var natManager = new NATManager(networkInfo.DefaultPort); #if DEBUG if (networkInfo.IsLANHost) { ipAddress = natManager.InternalIPAddress ?? IPAddress.Loopback; networkInfo.PeersToFind = 0; } else if (networkInfo.IsLANClient) { var ipAddressStr = (natManager.InternalIPAddress ?? IPAddress.Loopback).ToString(); networkInfo.PeersToFind = 1; if (networkInfo.Seeds.Count == 0 && !networkInfo.Seeds.Contains(ipAddressStr)) { networkInfo.Seeds.Add(ipAddressStr); } } else #endif if (!string.IsNullOrEmpty(networkInfo.ExternalIPAddress)) { ipAddress = IPAddress.Parse(networkInfo.ExternalIPAddress); } else if (networkInfo.DisableUPnP) { StatusMessageProducer.OutboundStatus = OutboundStatusEnum.Disabled; } else { StatusMessageProducer.OutboundStatus = OutboundStatusEnum.Initializing; await natManager.Init(); if (natManager.DeviceFound && natManager.Mapped.Value && natManager.ExternalIPVerified.HasValue && natManager.ExternalIPVerified.Value) { StatusMessageProducer.OutboundStatus = OutboundStatusEnum.HasValidAddress; ipAddress = natManager.ExternalIPAddress; } else { StatusMessageProducer.OutboundStatus = OutboundStatusEnum.HasInvalidAddress; } } _BroadcastHubBehavior = new BroadcastHubBehavior(); _MinerBehavior = new MinerBehavior(); _NodeConnectionParameters.TemplateBehaviors.Add(_BroadcastHubBehavior); _NodeConnectionParameters.TemplateBehaviors.Add(_MinerBehavior); _NodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(_BlockChain, _BroadcastHubBehavior.BroadcastHub)); _NodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(_BlockChain)); AddressManagerBehavior.GetAddrman(_NodeConnectionParameters).PeersToFind = networkInfo.PeersToFind; if (ipAddress != null) { _NodeConnectionParameters.TemplateBehaviors.Find <AddressManagerBehavior>().Mode = AddressManagerBehaviorMode.AdvertizeDiscover; //parameters.Advertize = true; _NodeConnectionParameters.AddressFrom = new System.Net.IPEndPoint(ipAddress, networkInfo.DefaultPort); } else { _NodeConnectionParameters.TemplateBehaviors.Find <AddressManagerBehavior>().Mode = AddressManagerBehaviorMode.Discover; } if (ipAddress != null) { _Server = new Server(ipAddress, networkInfo, _NodeConnectionParameters); OwnResource(_Server); if (_Server.Start()) { NodeServerTrace.Information($"Server started at {ipAddress}:{networkInfo.DefaultPort}"); StatusMessageProducer.OutboundStatus = OutboundStatusEnum.Accepting; } else { NodeServerTrace.Information($"Could not start server at {ipAddress}:{networkInfo.DefaultPort}"); } } if (networkInfo.Seeds.Count == 0) { NodeServerTrace.Information("No seeds defined"); } else { _NodesGroup = new NodesGroup(networkInfo, _NodeConnectionParameters); OwnResource(_NodesGroup); _NodesGroup.AllowSameGroup = true; //TODO _NodesGroup.MaximumNodeConnection = networkInfo.MaximumNodeConnection; #if TRACE _NodesGroup.ConnectedNodes.Added += (object sender, NodeEventArgs e) => { NodeServerTrace.Information("Peer found: " + e.Node.RemoteSocketAddress + ":" + e.Node.RemoteSocketPort); }; #endif _NodesGroup.Connect(); } }
public async Task SendTestsAsync() { (string password, IRPCClient rpc, Network network, _, ServiceConfiguration serviceConfiguration, BitcoinStore bitcoinStore, Backend.Global global) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1); bitcoinStore.IndexStore.NewFilter += Common.Wallet_NewFilterProcessed; // Create the services. // 1. Create connection service. var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements); nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync()); // 2. Create mempool service. Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync(); node.Behaviors.Add(bitcoinStore.CreateUntrustedP2pBehavior()); // 3. Create wasabi synchronizer service. var synchronizer = new WasabiSynchronizer(rpc.Network, bitcoinStore, new Uri(RegTestFixture.BackendEndPoint), null); // 4. Create key manager service. var keyManager = KeyManager.CreateNew(out _, password); // 5. Create wallet service. var workDir = Tests.Common.GetWorkDir(); CachedBlockProvider blockProvider = new CachedBlockProvider( new P2pBlockProvider(nodes, null, synchronizer, serviceConfiguration, network), bitcoinStore.BlockRepository); var walletManager = new WalletManager(network, new WalletDirectories(workDir)); walletManager.RegisterServices(bitcoinStore, synchronizer, nodes, serviceConfiguration, synchronizer, blockProvider); // Get some money, make it confirm. var key = keyManager.GetNextReceiveKey("foo label", out _); var key2 = keyManager.GetNextReceiveKey("foo label", out _); var txId = await rpc.SendToAddressAsync(key.GetP2wpkhAddress(network), Money.Coins(1m)); Assert.NotNull(txId); await rpc.GenerateAsync(1); var txId2 = await rpc.SendToAddressAsync(key2.GetP2wpkhAddress(network), Money.Coins(1m)); Assert.NotNull(txId2); await rpc.GenerateAsync(1); try { Interlocked.Exchange(ref Common.FiltersProcessedByWalletCount, 0); nodes.Connect(); // Start connection service. node.VersionHandshake(); // Start mempool service. synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service. // Wait until the filter our previous transaction is present. var blockCount = await rpc.GetBlockCountAsync(); await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount); var wallet = await walletManager.AddAndStartWalletAsync(keyManager); var broadcaster = new TransactionBroadcaster(network, bitcoinStore, synchronizer, nodes, walletManager, rpc); var waitCount = 0; while (wallet.Coins.Sum(x => x.Amount) == Money.Zero) { await Task.Delay(1000); waitCount++; if (waitCount >= 21) { Logger.LogInfo("Funding transaction to the wallet did not arrive."); return; // Very rarely this test fails. I have no clue why. Probably because all these RegTests are interconnected, anyway let's not bother the CI with it. } } var scp = new Key().ScriptPubKey; var res2 = wallet.BuildTransaction(password, new PaymentIntent(scp, Money.Coins(0.05m), label: "foo"), FeeStrategy.CreateFromConfirmationTarget(5), allowUnconfirmed: false); Assert.NotNull(res2.Transaction); Assert.Single(res2.OuterWalletOutputs); Assert.Equal(scp, res2.OuterWalletOutputs.Single().ScriptPubKey); Assert.Single(res2.InnerWalletOutputs); Assert.True(res2.Fee > Money.Satoshis(2 * 100)); // since there is a sanity check of 2sat/vb in the server Assert.InRange(res2.FeePercentOfSent, 0, 1); Assert.Single(res2.SpentCoins); var spentCoin = Assert.Single(res2.SpentCoins); Assert.Contains(new[] { key.P2wpkhScript, key2.P2wpkhScript }, x => x == spentCoin.ScriptPubKey); Assert.Equal(Money.Coins(1m), res2.SpentCoins.Single().Amount); Assert.False(res2.SpendsUnconfirmed); await broadcaster.SendTransactionAsync(res2.Transaction); Assert.Contains(res2.InnerWalletOutputs.Single(), wallet.Coins); #region Basic Script receive = keyManager.GetNextReceiveKey("Basic", out _).P2wpkhScript; Money amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 2; var res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); foreach (SmartCoin coin in res.SpentCoins) { Assert.False(coin.CoinJoinInProgress); Assert.True(coin.Confirmed); Assert.Null(coin.SpenderTransactionId); Assert.True(coin.Unspent); } Assert.Equal(2, res.InnerWalletOutputs.Count()); Assert.Empty(res.OuterWalletOutputs); var activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); var changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Equal(amountToSend, activeOutput.Amount); if (res.SpentCoins.Sum(x => x.Amount) - activeOutput.Amount == res.Fee) // this happens when change is too small { Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == activeOutput.Amount); Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}"); } Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); var foundReceive = false; Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2); foreach (var output in res.Transaction.Transaction.Outputs) { if (output.ScriptPubKey == receive) { foundReceive = true; Assert.Equal(amountToSend, output.Value); } } Assert.True(foundReceive); await broadcaster.SendTransactionAsync(res.Transaction); #endregion Basic #region SubtractFeeFromAmount receive = keyManager.GetNextReceiveKey("SubtractFeeFromAmount", out _).P2wpkhScript; amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 3; res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, subtractFee: true, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Equal(2, res.InnerWalletOutputs.Count()); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Equal(amountToSend - res.Fee, activeOutput.Amount); Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount); Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); foundReceive = false; Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2); foreach (var output in res.Transaction.Transaction.Outputs) { if (output.ScriptPubKey == receive) { foundReceive = true; Assert.Equal(amountToSend - res.Fee, output.Value); } } Assert.True(foundReceive); #endregion SubtractFeeFromAmount #region LowFee res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Equal(2, res.InnerWalletOutputs.Count()); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Equal(amountToSend, activeOutput.Amount); Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount); Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); foundReceive = false; Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2); foreach (var output in res.Transaction.Transaction.Outputs) { if (output.ScriptPubKey == receive) { foundReceive = true; Assert.Equal(amountToSend, output.Value); } } Assert.True(foundReceive); #endregion LowFee #region MediumFee res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.OneDayConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Equal(2, res.InnerWalletOutputs.Count()); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Equal(amountToSend, activeOutput.Amount); Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount); Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); foundReceive = false; Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2); foreach (var output in res.Transaction.Transaction.Outputs) { if (output.ScriptPubKey == receive) { foundReceive = true; Assert.Equal(amountToSend, output.Value); } } Assert.True(foundReceive); #endregion MediumFee #region HighFee res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Equal(2, res.InnerWalletOutputs.Count()); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Equal(amountToSend, activeOutput.Amount); Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount); Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); foundReceive = false; Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2); foreach (var output in res.Transaction.Transaction.Outputs) { if (output.ScriptPubKey == receive) { foundReceive = true; Assert.Equal(amountToSend, output.Value); } } Assert.True(foundReceive); Assert.InRange(res.Fee, Money.Zero, res.Fee); Assert.InRange(res.Fee, res.Fee, res.Fee); await broadcaster.SendTransactionAsync(res.Transaction); #endregion HighFee #region MaxAmount receive = keyManager.GetNextReceiveKey("MaxAmount", out _).P2wpkhScript; res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Single(res.InnerWalletOutputs); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(); Assert.Equal(receive, activeOutput.ScriptPubKey); Assert.Single(res.Transaction.Transaction.Outputs); var maxBuiltTxOutput = res.Transaction.Transaction.Outputs.Single(); Assert.Equal(receive, maxBuiltTxOutput.ScriptPubKey); Assert.Equal(wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) - res.Fee, maxBuiltTxOutput.Value); await broadcaster.SendTransactionAsync(res.Transaction); #endregion MaxAmount #region InputSelection receive = keyManager.GetNextReceiveKey("InputSelection", out _).P2wpkhScript; var inputCountBefore = res.SpentCoins.Count(); res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true, allowedInputs: wallet.Coins.Where(x => !x.Unavailable).Select(x => x.OutPoint).Take(1)); Assert.Single(res.InnerWalletOutputs); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); Assert.True(inputCountBefore >= res.SpentCoins.Count()); Assert.Equal(res.SpentCoins.Count(), res.Transaction.Transaction.Inputs.Count); Assert.Equal(receive, activeOutput.ScriptPubKey); Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogInfo($"TxId: {res.Transaction.GetHash()}"); Assert.Single(res.Transaction.Transaction.Outputs); res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true, allowedInputs: new[] { res.SpentCoins.Select(x => x.OutPoint).First() }); Assert.Single(res.InnerWalletOutputs); Assert.Empty(res.OuterWalletOutputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); Assert.Single(res.Transaction.Transaction.Inputs); Assert.Single(res.Transaction.Transaction.Outputs); Assert.Single(res.SpentCoins); #endregion InputSelection #region Labeling Script receive2 = keyManager.GetNextReceiveKey("foo", out _).P2wpkhScript; res = wallet.BuildTransaction(password, new PaymentIntent(receive2, MoneyRequest.CreateAllRemaining(), "my label"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Single(res.InnerWalletOutputs); Assert.Equal("foo, my label", res.InnerWalletOutputs.Single().Label); amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 3; res = wallet.BuildTransaction( password, new PaymentIntent( new DestinationRequest(new Key(), amountToSend, label: "outgoing"), new DestinationRequest(new Key(), amountToSend, label: "outgoing2")), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true); Assert.Single(res.InnerWalletOutputs); Assert.Equal(2, res.OuterWalletOutputs.Count()); IEnumerable <string> change = res.InnerWalletOutputs.Single().Label.Labels; Assert.Contains("outgoing", change); Assert.Contains("outgoing2", change); await broadcaster.SendTransactionAsync(res.Transaction); IEnumerable <SmartCoin> unconfirmedCoins = wallet.Coins.Where(x => x.Height == Height.Mempool).ToArray(); IEnumerable <string> unconfirmedCoinLabels = unconfirmedCoins.SelectMany(x => x.Label.Labels).ToArray(); Assert.Contains("outgoing", unconfirmedCoinLabels); Assert.Contains("outgoing2", unconfirmedCoinLabels); IEnumerable <string> allKeyLabels = keyManager.GetKeys().SelectMany(x => x.Label.Labels); Assert.Contains("outgoing", allKeyLabels); Assert.Contains("outgoing2", allKeyLabels); Interlocked.Exchange(ref Common.FiltersProcessedByWalletCount, 0); await rpc.GenerateAsync(1); await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), 1); var bestHeight = new Height(bitcoinStore.SmartHeaderChain.TipHeight); IEnumerable <string> confirmedCoinLabels = wallet.Coins.Where(x => x.Height == bestHeight).SelectMany(x => x.Label.Labels); Assert.Contains("outgoing", confirmedCoinLabels); Assert.Contains("outgoing2", confirmedCoinLabels); allKeyLabels = keyManager.GetKeys().SelectMany(x => x.Label.Labels); Assert.Contains("outgoing", allKeyLabels); Assert.Contains("outgoing2", allKeyLabels); #endregion Labeling #region AllowedInputsDisallowUnconfirmed inputCountBefore = res.SpentCoins.Count(); receive = keyManager.GetNextReceiveKey("AllowedInputsDisallowUnconfirmed", out _).P2wpkhScript; var allowedInputs = wallet.Coins.Where(x => !x.Unavailable).Select(x => x.OutPoint).Take(1); var toSend = new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "fizz"); // covers: // disallow unconfirmed with allowed inputs res = wallet.BuildTransaction(password, toSend, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false, allowedInputs: allowedInputs); activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive); Assert.Single(res.InnerWalletOutputs); Assert.Empty(res.OuterWalletOutputs); Assert.Equal(receive, activeOutput.ScriptPubKey); Logger.LogDebug($"{nameof(res.Fee)}: {res.Fee}"); Logger.LogDebug($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %"); Logger.LogDebug($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}"); Logger.LogDebug($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}"); Logger.LogDebug($"TxId: {res.Transaction.GetHash()}"); Assert.True(inputCountBefore >= res.SpentCoins.Count()); Assert.False(res.SpendsUnconfirmed); Assert.Single(res.Transaction.Transaction.Inputs); Assert.Single(res.Transaction.Transaction.Outputs); Assert.Single(res.SpentCoins); Assert.True(inputCountBefore >= res.SpentCoins.Count()); Assert.Equal(res.SpentCoins.Count(), res.Transaction.Transaction.Inputs.Count); #endregion AllowedInputsDisallowUnconfirmed #region CustomChange // covers: // customchange // feePc > 1 var k1 = new Key(); var k2 = new Key(); res = wallet.BuildTransaction( password, new PaymentIntent( new DestinationRequest(k1, MoneyRequest.CreateChange()), new DestinationRequest(k2, Money.Coins(0.0003m), label: "outgoing")), FeeStrategy.TwentyMinutesConfirmationTargetStrategy); Assert.Contains(k1.ScriptPubKey, res.OuterWalletOutputs.Select(x => x.ScriptPubKey)); Assert.Contains(k2.ScriptPubKey, res.OuterWalletOutputs.Select(x => x.ScriptPubKey)); #endregion CustomChange #region FeePcHigh res = wallet.BuildTransaction( password, new PaymentIntent(new Key(), Money.Coins(0.0003m), label: "outgoing"), FeeStrategy.TwentyMinutesConfirmationTargetStrategy); Assert.True(res.FeePercentOfSent > 1); var newChangeK = keyManager.GenerateNewKey("foo", KeyState.Clean, isInternal: true); res = wallet.BuildTransaction( password, new PaymentIntent( new DestinationRequest(newChangeK.P2wpkhScript, MoneyRequest.CreateChange(), "boo"), new DestinationRequest(new Key(), Money.Coins(0.0003m), label: "outgoing")), FeeStrategy.TwentyMinutesConfirmationTargetStrategy); Assert.True(res.FeePercentOfSent > 1); Assert.Single(res.OuterWalletOutputs); Assert.Single(res.InnerWalletOutputs); SmartCoin changeRes = res.InnerWalletOutputs.Single(); Assert.Equal(newChangeK.P2wpkhScript, changeRes.ScriptPubKey); Assert.Equal(newChangeK.Label, changeRes.Label); Assert.Equal(KeyState.Clean, newChangeK.KeyState); // Still clean, because the tx wasn't yet propagated. #endregion FeePcHigh } finally { bitcoinStore.IndexStore.NewFilter -= Common.Wallet_NewFilterProcessed; await walletManager.RemoveAndStopAllAsync(CancellationToken.None); // Dispose wasabi synchronizer service. if (synchronizer is { })
public static void Initialize(Config config) { WalletService = null; ChaumianClient = null; Config = Guard.NotNull(nameof(config), config); string blindingPubKeyFile = Path.Combine(DataDir, $"BlindingPubKey{Network}.json"); if (File.Exists(blindingPubKeyFile)) { string blindingPubKeyJson = ""; blindingPubKeyJson = File.ReadAllText(blindingPubKeyFile); BlindingPubKey = BlindingRsaPubKey.CreateFromJson(blindingPubKeyJson); } else { if (Network == Network.Main) { BlindingPubKey = new BlindingRsaPubKey(new BigInteger("16421152619146079007287475569112871971988560541093277613438316709041030720662622782033859387192362542996510605015506477964793447620206674394713753349543444988246276357919473682408472170521463339860947351211455351029147665615454176157348164935212551240942809518428851690991984017733153078846480521091423447691527000770982623947706172997649440619968085147635776736938871139581019988225202983052255684151711253254086264386774936200194229277914886876824852466823571396538091430866082004097086602287294474304344865162932126041736158327600847754258634325228417149098062181558798532036659383679712667027126535424484318399849"), new BigInteger("65537")); } else { BlindingPubKey = new BlindingRsaPubKey(new BigInteger("19473594448380717274202325076521698699373476167359253614775896809797414915031772455344343455269320444157176520539924715307970060890094127521516100754263825112231545354422893125394219335109864514907655429499954825469485252969706079992227103439161156022844535556626007277544637236136559868400854764962522288139619969507311597914908752685925185380735570791798593290356424409633800092336087046668579610273133131498947353719917407262847070395909920415822288443947309434039008038907229064999576278651443575362470457496666718250346530518268694562965606704838796709743032825816642704620776596590683042135764246115456630753521"), new BigInteger("65537")); } Directory.CreateDirectory(DataDir); File.WriteAllText(blindingPubKeyFile, BlindingPubKey.ToJson()); } var addressManagerFolderPath = Path.Combine(DataDir, "AddressManager"); AddressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{Network}.dat"); var blocksFolderPath = Path.Combine(DataDir, $"Blocks{Network}"); var connectionParameters = new NodeConnectionParameters(); AddressManager = null; try { AddressManager = AddressManager.LoadPeerFile(AddressManagerFilePath); Logger.LogInfo <AddressManager>($"Loaded {nameof(AddressManager)} from `{AddressManagerFilePath}`."); } catch (DirectoryNotFoundException ex) { Logger.LogInfo <AddressManager>($"{nameof(AddressManager)} did not exist at `{AddressManagerFilePath}`. Initializing new one."); Logger.LogTrace <AddressManager>(ex); AddressManager = new AddressManager(); } catch (FileNotFoundException ex) { Logger.LogInfo <AddressManager>($"{nameof(AddressManager)} did not exist at `{AddressManagerFilePath}`. Initializing new one."); Logger.LogTrace <AddressManager>(ex); AddressManager = new AddressManager(); } connectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(AddressManager)); MemPoolService = new MemPoolService(); connectionParameters.TemplateBehaviors.Add(new MemPoolBehavior(MemPoolService)); Nodes = new NodesGroup(Network, connectionParameters, new NodeRequirement { RequiredServices = NodeServices.Network, MinVersion = Constants.ProtocolVersion_WITNESS_VERSION }); var indexFilePath = Path.Combine(DataDir, $"Index{Network}.dat"); IndexDownloader = new IndexDownloader(Network, indexFilePath, Config.GetCurrentBackendUri()); Nodes.Connect(); Logger.LogInfo("Start connecting to nodes..."); IndexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(21)); Logger.LogInfo("Start synchronizing filters..."); }
public async Task TestServicesAsync(string networkString) { var network = Network.GetNetwork(networkString); var blocksToDownload = new HashSet <uint256>(); if (network == Network.Main) { blocksToDownload.Add(new uint256("00000000000000000037c2de35bd85f3e57f14ddd741ce6cee5b28e51473d5d0")); blocksToDownload.Add(new uint256("000000000000000000115315a43cb0cdfc4ea54a0e92bed127f4e395e718d8f9")); blocksToDownload.Add(new uint256("00000000000000000011b5b042ad0522b69aae36f7de796f563c895714bbd629")); } else if (network == Network.TestNet) { blocksToDownload.Add(new uint256("0000000097a664c4084b49faa6fd4417055cb8e5aac480abc31ddc57a8208524")); blocksToDownload.Add(new uint256("000000009ed5b82259ecd2aa4cd1f119db8da7a70e7ea78d9c9f603e01f93bcc")); blocksToDownload.Add(new uint256("00000000e6da8c2da304e9f5ad99c079df2c3803b49efded3061ecaf206ddc66")); } else { throw new NotSupportedException(network.ToString()); } var addressManagerFolderPath = Path.Combine(Global.Instance.DataDir, "AddressManager"); var addressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{network}.dat"); var blocksFolderPath = Path.Combine(Global.Instance.DataDir, "Blocks", network.ToString()); var connectionParameters = new NodeConnectionParameters(); AddressManager addressManager = null; try { addressManager = AddressManager.LoadPeerFile(addressManagerFilePath); Logger.LogInfo <AddressManager>($"Loaded {nameof(AddressManager)} from `{addressManagerFilePath}`."); } catch (DirectoryNotFoundException ex) { Logger.LogInfo <AddressManager>($"{nameof(AddressManager)} did not exist at `{addressManagerFilePath}`. Initializing new one."); Logger.LogTrace <AddressManager>(ex); addressManager = new AddressManager(); } catch (FileNotFoundException ex) { Logger.LogInfo <AddressManager>($"{nameof(AddressManager)} did not exist at `{addressManagerFilePath}`. Initializing new one."); Logger.LogTrace <AddressManager>(ex); addressManager = new AddressManager(); } connectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); var memPoolService = new MemPoolService(); connectionParameters.TemplateBehaviors.Add(new MemPoolBehavior(memPoolService)); var nodes = new NodesGroup(network, connectionParameters, requirements: Helpers.Constants.NodeRequirements); BitcoinStore bitcoinStore = new BitcoinStore(); await bitcoinStore.InitializeAsync(Path.Combine(Global.Instance.DataDir, nameof(TestServicesAsync)), network); KeyManager keyManager = KeyManager.CreateNew(out _, "password"); WasabiSynchronizer syncer = new WasabiSynchronizer(network, bitcoinStore, new Uri("http://localhost:12345"), Global.Instance.TorSocks5Endpoint); WalletService walletService = new WalletService( bitcoinStore, keyManager, syncer, new CcjClient(syncer, network, keyManager, new Uri("http://localhost:12345"), Global.Instance.TorSocks5Endpoint), memPoolService, nodes, Global.Instance.DataDir, new ServiceConfiguration(50, 2, 21, 50, new IPEndPoint(IPAddress.Loopback, network.DefaultPort), Money.Coins(0.0001m))); Assert.True(Directory.Exists(blocksFolderPath)); try { nodes.ConnectedNodes.Added += ConnectedNodes_Added; nodes.ConnectedNodes.Removed += ConnectedNodes_Removed; memPoolService.TransactionReceived += MemPoolService_TransactionReceived; nodes.Connect(); // Using the interlocked, not because it makes sense in this context, but to // set an example that these values are often concurrency sensitive var times = 0; while (Interlocked.Read(ref _nodeCount) < 3) { if (times > 4200) // 7 minutes { throw new TimeoutException($"Connection test timed out."); } await Task.Delay(100); times++; } times = 0; while (Interlocked.Read(ref _mempoolTransactionCount) < 3) { if (times > 3000) // 3 minutes { throw new TimeoutException($"{nameof(MemPoolService)} test timed out."); } await Task.Delay(100); times++; } foreach (var hash in blocksToDownload) { using (var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3))) { var block = await walletService.GetOrDownloadBlockAsync(hash, cts.Token); Assert.True(File.Exists(Path.Combine(blocksFolderPath, hash.ToString()))); Logger.LogInfo <P2pTests>($"Full block is downloaded: {hash}."); } } } finally { nodes.ConnectedNodes.Added -= ConnectedNodes_Added; nodes.ConnectedNodes.Removed -= ConnectedNodes_Removed; memPoolService.TransactionReceived -= MemPoolService_TransactionReceived; // So next test will download the block. foreach (var hash in blocksToDownload) { await walletService?.DeleteBlockAsync(hash); } if (walletService != null) { await walletService.StopAsync(); } if (Directory.Exists(blocksFolderPath)) { Directory.Delete(blocksFolderPath, recursive: true); } IoHelpers.EnsureContainingDirectoryExists(addressManagerFilePath); addressManager?.SavePeerFile(addressManagerFilePath, network); Logger.LogInfo <P2pTests>($"Saved {nameof(AddressManager)} to `{addressManagerFilePath}`."); nodes?.Dispose(); await syncer?.StopAsync(); } }
public async Task StartAsync() { if (_Disposed) { throw new ObjectDisposedException(nameof(NBxplorerInitializer)); } _Starting.Reset(); try { if (_Network.IsRegTest) { if (await _RPC.GetBlockCountAsync() < 100) { Logs.Configuration.LogInformation($"Less than 100 blocks, mining some block for regtest"); await _RPC.GenerateAsync(101); } else { var header = await _RPC.GetBlockHeaderAsync(await _RPC.GetBestBlockHashAsync()); if ((DateTimeOffset.UtcNow - header.BlockTime) > TimeSpan.FromSeconds(24 * 60 * 60)) { Logs.Configuration.LogInformation($"It has been a while nothing got mined on regtest... mining 10 blocks"); await _RPC.GenerateAsync(10); } } } var cachePath = Path.Combine(_Configuration.DataDir, "chain.dat"); if (_Configuration.CacheChain && File.Exists(cachePath)) { Logs.Configuration.LogInformation($"Loading chain from cache..."); _Chain.Load(File.ReadAllBytes(cachePath)); Logs.Configuration.LogInformation($"Height: " + _Chain.Height); } var heightBefore = _Chain.Height; Logs.Configuration.LogInformation($"Loading chain from node..."); using (var node = Node.Connect(_Network.Network, _Configuration.NodeEndpoint)) { var cts = new CancellationTokenSource(); cts.CancelAfter(5000); node.VersionHandshake(cts.Token); node.SynchronizeChain(_Chain); } Logs.Configuration.LogInformation("Height: " + _Chain.Height); if (_Configuration.CacheChain && heightBefore != _Chain.Height) { Logs.Configuration.LogInformation($"Saving chain to cache..."); var ms = new MemoryStream(); _Chain.WriteTo(ms); File.WriteAllBytes(cachePath, ms.ToArray()); Logs.Configuration.LogInformation($"Saved"); } AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(_Configuration.NodeEndpoint), IPAddress.Loopback); NodesGroup group = new NodesGroup(_Network.Network, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new ExplorerBehavior(_Repository, _Chain, _Invoker, _Events) { StartHeight = _Configuration.StartHeight }, new ChainBehavior(_Chain) { CanRespondToGetHeaders = false } } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; group.Connect(); _Group = group; } finally { _Starting.Set(); } }
/// <summary> /// Connect the wallet with the given connection parameters /// </summary> /// <param name="group">The group to use</param> public void Connect(NodesGroup group) { if (group == null) { throw new ArgumentNullException("group"); } if (State != WalletState.Created) { throw new InvalidOperationException("The wallet is already connecting or connected"); } var parameters = group.NodeConnectionParameters; group.Requirements.MinVersion = ProtocolVersion.PROTOCOL_VERSION; group.Requirements.RequiredServices |= NodeServices.Network; var chain = parameters.TemplateBehaviors.Find <ChainBehavior>(); if (chain == null) { chain = new ChainBehavior(new ConcurrentChain(_Parameters.Network)); parameters.TemplateBehaviors.Add(chain); } if (chain.Chain.Genesis.HashBlock != _Parameters.Network.GetGenesis().GetHash()) { throw new InvalidOperationException("ChainBehavior with invalid network chain detected"); } var addrman = parameters.TemplateBehaviors.Find <AddressManagerBehavior>(); if (addrman == null) { addrman = new AddressManagerBehavior(new AddressManager()); parameters.TemplateBehaviors.Add(addrman); } var tracker = parameters.TemplateBehaviors.Find <TrackerBehavior>(); if (tracker == null) { tracker = new TrackerBehavior(new Tracker(), chain.Chain); parameters.TemplateBehaviors.Add(tracker); } _Chain = chain.Chain; _AddressManager = addrman.AddressManager; _Tracker = tracker.Tracker; _TrackerBehavior = tracker; _Group = group; if (AddKnownScriptToTracker()) { _Group.Purge("Bloom filter renew"); } _State = WalletState.Disconnected; _Group.Connect(); _Group.ConnectedNodes.Added += ConnectedNodes_Added; _Group.ConnectedNodes.Removed += ConnectedNodes_Added; foreach (var node in _Group.ConnectedNodes) { node.Behaviors.Find <TrackerBehavior>().Scan(_ScanLocation, Created); } }
public void CanDiscoverPeers() { int SERVER_COUNT = 4; int FIRST_SERVER = 0; int SECOND_SERVER = 1; int LAST_SERVER = 2; int NEW_SERVER = 3; var ToBeConnected = new List <NodeServer>(); var ToBeDiscovered = new List <Node>(); WithServerSet(SERVER_COUNT, servers => { servers.SeedServerIndex = LAST_SERVER; //TODO ToBeConnected.Add(servers[FIRST_SERVER]); ToBeConnected.Add(servers[SECOND_SERVER]); ToBeConnected.Add(servers[LAST_SERVER]); ToBeDiscovered.Add(Handshake(servers[FIRST_SERVER], servers[SECOND_SERVER])); Trace.Information("Handshake First -> Second"); ToBeDiscovered.Add(Handshake(servers[SECOND_SERVER], servers[LAST_SERVER])); Trace.Information("Handshake Second -> Last"); #region Setup Parameters for NodeGroup AddressManager addressManager = new AddressManager(); addressManager.PeersToFind = ToBeConnected.Count * 2; // nodes answer to GetAddr with random nodes, so keep trying until we get all expected items NodeConnectionParameters parameters = new NodeConnectionParameters(); parameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); parameters.AddressFrom = servers[NEW_SERVER].ExternalEndpoint; //TODO NodesGroup nodesGroup = new NodesGroup(servers.Network, parameters); nodesGroup.AllowSameGroup = true; //TODO nodesGroup.MaximumNodeConnection = ToBeConnected.Count; //TODO #endregion nodesGroup.Connect(); AutoResetEvent waitForDiscoveredAll = new AutoResetEvent(false); var discoveredAll = false; nodesGroup.ConnectedNodes.Added += (object sender, NodeEventArgs e) => { Console.WriteLine($"\n\n\nPeer found: {e.Node.Peer.Endpoint}\n\n\n"); Node node = ToBeDiscovered.Find(n => n.MyVersion.AddressFrom.Equals(e.Node.Peer.Endpoint)); if (node != null && ToBeDiscovered.Contains(node)) { ToBeDiscovered.Remove(node); if (ToBeDiscovered.Count == 0) { discoveredAll = true; waitForDiscoveredAll.Set(); } } }; Assert.True(waitForDiscoveredAll.WaitOne(30000)); Assert.True(discoveredAll); }); }
static void Main(string[] args) { var connectionParameters = new NodeConnectionParameters() { ReceiveBufferSize = 1048576, SendBufferSize = 1048576 }; var addressManagerFilePath = "AddressManager.dat"; AddressManager addressManager; try { addressManager = AddressManager.LoadPeerFile(addressManagerFilePath); Console.WriteLine($"Loaded {nameof(AddressManager)} from `{addressManagerFilePath}`."); } catch (FileNotFoundException) { Console.WriteLine($"{nameof(AddressManager)} did not exist at `{addressManagerFilePath}`. Initializing new one."); addressManager = new AddressManager(); } connectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addressManager)); var nodes = new NodesGroup(Network.Main, connectionParameters, new NodeRequirement { RequiredServices = NodeServices.Network, MinVersion = ProtocolVersion.WITNESS_VERSION }); Console.WriteLine("Start connecting to nodes..."); nodes.Connect(); Console.WriteLine(); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.AddressFrom)}: {connectionParameters.AddressFrom}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.IsRelay)}: {connectionParameters.IsRelay}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.PreferredTransactionOptions)}: {connectionParameters.PreferredTransactionOptions}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.ReceiveBufferSize)}: {connectionParameters.ReceiveBufferSize}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.ReuseBuffer)}: {connectionParameters.ReuseBuffer}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.SendBufferSize)}: {connectionParameters.SendBufferSize}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.Services)}: {connectionParameters.Services}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.TemplateBehaviors)}.Count(): {connectionParameters.TemplateBehaviors.Count()}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.UserAgent)}: {connectionParameters.UserAgent}"); Console.WriteLine($"{nameof(connectionParameters)}.{nameof(connectionParameters.Version)}: {connectionParameters.Version}"); Console.WriteLine(); Console.WriteLine($"{nameof(addressManager)}.{nameof(addressManager.Count)}: {addressManager.Count}"); Console.WriteLine($"{nameof(addressManager)}.GetAddr().Count(): {addressManager.GetAddr().Count()}"); Console.WriteLine($"{nameof(addressManager)}.GetSerializedSize(): {addressManager.GetSerializedSize()}"); Console.WriteLine(); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.AllowSameGroup)}: {nodes.AllowSameGroup}"); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.ConnectedNodes)}.{nameof(nodes.ConnectedNodes.Count)}: {nodes.ConnectedNodes.Count}"); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.MaximumNodeConnection)}: {nodes.MaximumNodeConnection}"); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.Requirements)}.{nameof(nodes.Requirements.MinVersion)}: {nodes.Requirements.MinVersion}"); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.Requirements)}.{nameof(nodes.Requirements.RequiredServices)}: {nodes.Requirements.RequiredServices}"); Console.WriteLine($"{nameof(nodes)}.{nameof(nodes.Requirements)}.{nameof(nodes.Requirements.SupportSPV)}: {nodes.Requirements.SupportSPV}"); Console.WriteLine(); try { int i = 0; while (i < 42) // 7 minutes { Console.WriteLine($"Witing for a connection for {i / (double)6} minutes...."); if (nodes.ConnectedNodes.Count >= 1) { Console.WriteLine("SUCCESSFULLY FOUND A CONNECTION"); return; } Task.Delay(TimeSpan.FromSeconds(10)).GetAwaiter().GetResult(); i++; } throw new TimeoutException($"DID NOT FIND A CONNECTION within {i / (double)6} minutes."); } finally { Console.WriteLine($"Saving {nameof(AddressManager)} to `{addressManagerFilePath}`."); addressManager.SavePeerFile(addressManagerFilePath, Network.Main); nodes.Dispose(); } }
public async Task BuildTransactionValidationsTestAsync() { (string password, IRPCClient rpc, Network network, Coordinator coordinator, ServiceConfiguration serviceConfiguration, BitcoinStore bitcoinStore, Backend.Global global) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1); // Create the services. // 1. Create connection service. var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements); nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync()); // 2. Create mempool service. Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync(); node.Behaviors.Add(bitcoinStore.CreateUntrustedP2pBehavior()); // 3. Create wasabi synchronizer service. var synchronizer = new WasabiSynchronizer(rpc.Network, bitcoinStore, new Uri(RegTestFixture.BackendEndPoint), null); // 4. Create key manager service. var keyManager = KeyManager.CreateNew(out _, password); // 5. Create wallet service. var workDir = Common.GetWorkDir(); var blocksFolderPath = Path.Combine(workDir, "Blocks", network.ToString()); CachedBlockProvider blockProvider = new CachedBlockProvider( new P2pBlockProvider(nodes, null, synchronizer, serviceConfiguration, network), new FileSystemBlockRepository(blocksFolderPath, network)); using var wallet = Wallet.CreateAndRegisterServices(network, bitcoinStore, keyManager, synchronizer, nodes, workDir, serviceConfiguration, synchronizer, blockProvider); wallet.NewFilterProcessed += Common.Wallet_NewFilterProcessed; var scp = new Key().ScriptPubKey; var validIntent = new PaymentIntent(scp, Money.Coins(1)); var invalidIntent = new PaymentIntent( new DestinationRequest(scp, Money.Coins(10 * 1000 * 1000)), new DestinationRequest(scp, Money.Coins(12 * 1000 * 1000))); Assert.Throws <OverflowException>(() => new PaymentIntent( new DestinationRequest(scp, Money.Satoshis(long.MaxValue)), new DestinationRequest(scp, Money.Satoshis(long.MaxValue)), new DestinationRequest(scp, Money.Satoshis(5)))); Logger.TurnOff(); Assert.Throws <ArgumentNullException>(() => wallet.BuildTransaction(null, null, FeeStrategy.CreateFromConfirmationTarget(4))); // toSend cannot have a null element Assert.Throws <ArgumentNullException>(() => wallet.BuildTransaction(null, new PaymentIntent(new[] { (DestinationRequest)null }), FeeStrategy.CreateFromConfirmationTarget(0))); // toSend cannot have a zero element Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(null, new PaymentIntent(Array.Empty <DestinationRequest>()), FeeStrategy.SevenDaysConfirmationTargetStrategy)); // feeTarget has to be in the range 0 to 1008 Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.CreateFromConfirmationTarget(-10))); Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.CreateFromConfirmationTarget(2000))); // toSend amount sum has to be in range 0 to 2099999997690000 Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, invalidIntent, FeeStrategy.TwentyMinutesConfirmationTargetStrategy)); // toSend negative sum amount Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, new PaymentIntent(scp, Money.Satoshis(-10000)), FeeStrategy.TwentyMinutesConfirmationTargetStrategy)); // toSend negative operation amount Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction( null, new PaymentIntent( new DestinationRequest(scp, Money.Satoshis(20000)), new DestinationRequest(scp, Money.Satoshis(-10000))), FeeStrategy.TwentyMinutesConfirmationTargetStrategy)); // allowedInputs cannot be empty Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowedInputs: Array.Empty <OutPoint>())); // "Only one element can contain the AllRemaining flag. Assert.Throws <ArgumentException>(() => wallet.BuildTransaction( password, new PaymentIntent( new DestinationRequest(scp, MoneyRequest.CreateAllRemaining(), "zero"), new DestinationRequest(scp, MoneyRequest.CreateAllRemaining(), "zero")), FeeStrategy.SevenDaysConfirmationTargetStrategy, false)); // Get some money, make it confirm. var txId = await rpc.SendToAddressAsync(keyManager.GetNextReceiveKey("foo", out _).GetP2wpkhAddress(network), Money.Coins(1m)); // Generate some coins await rpc.GenerateAsync(2); try { nodes.Connect(); // Start connection service. node.VersionHandshake(); // Start mempool service. synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service. // Wait until the filter our previous transaction is present. var blockCount = await rpc.GetBlockCountAsync(); await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount); using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30))) { await wallet.StartAsync(cts.Token); // Initialize wallet service. } // subtract Fee from amount index with no enough money var operations = new PaymentIntent( new DestinationRequest(scp, Money.Coins(1m), subtractFee: true), new DestinationRequest(scp, Money.Coins(0.5m))); Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false)); // No enough money (only one confirmed coin, no unconfirmed allowed) operations = new PaymentIntent(scp, Money.Coins(1.5m)); Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy)); // No enough money (only one confirmed coin, unconfirmed allowed) Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true)); // Add new money with no confirmation var txId2 = await rpc.SendToAddressAsync(keyManager.GetNextReceiveKey("bar", out _).GetP2wpkhAddress(network), Money.Coins(2m)); await Task.Delay(1000); // Wait tx to arrive and get processed. // Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are NOT allowed) Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false)); // Enough money (one unconfirmed coin, unconfirmed are allowed) var btx = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true); var spentCoin = Assert.Single(btx.SpentCoins); Assert.False(spentCoin.Confirmed); // Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are allowed) operations = new PaymentIntent(scp, Money.Coins(2.5m)); btx = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true); Assert.Equal(2, btx.SpentCoins.Count()); Assert.Equal(1, btx.SpentCoins.Count(c => c.Confirmed)); Assert.Equal(1, btx.SpentCoins.Count(c => !c.Confirmed)); // Only one operation with AllRemainingFlag Assert.Throws <ArgumentException>(() => wallet.BuildTransaction( null, new PaymentIntent( new DestinationRequest(scp, MoneyRequest.CreateAllRemaining()), new DestinationRequest(scp, MoneyRequest.CreateAllRemaining())), FeeStrategy.TwentyMinutesConfirmationTargetStrategy)); Logger.TurnOn(); operations = new PaymentIntent(scp, Money.Coins(0.5m)); btx = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy); } finally { await wallet.StopAsync(CancellationToken.None); // Dispose wasabi synchronizer service. if (synchronizer is { })
public void Listen(ConcurrentChain chain = null) { ListenerTrace.Info($"Connecting to node {_Configuration.Indexer.Node}"); var ip = Utils.ParseIpEndpoint(_Configuration.Indexer.Node, Configuration.Indexer.Network.DefaultPort); ListenerTrace.Info($"Connecting to node ip {ip.ToString()}"); var node = Node.Connect(Configuration.Indexer.Network, ip); ListenerTrace.Info($"Connected, trying handshake..."); node.VersionHandshake(); ListenerTrace.Info($"Hanshaked"); node.Disconnect(); _Chain = new ConcurrentChain(_Configuration.Indexer.Network); _Indexer = Configuration.Indexer.CreateIndexer(); if (chain == null) { chain = new ConcurrentChain(_Configuration.Indexer.Network); } _Chain = chain; ListenerTrace.Info("Fetching headers from " + _Chain.Tip.Height + " (from azure)"); var client = Configuration.Indexer.CreateIndexerClient(); client.SynchronizeChain(chain); ListenerTrace.Info("Headers fetched tip " + _Chain.Tip.Height); _Disposables.Add(_IndexerScheduler = new CustomThreadPoolTaskScheduler(50, 100, "Indexing Threads")); _Indexer.TaskScheduler = _IndexerScheduler; _Group = new NodesGroup(Configuration.Indexer.Network); _Disposables.Add(_Group); _Group.AllowSameGroup = true; _Group.MaximumNodeConnection = 2; AddressManager addrman = new AddressManager(); addrman.Add(new NetworkAddress(ip), IPAddress.Parse("127.0.0.1")); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addrman) { Mode = AddressManagerBehaviorMode.None }); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(_Chain) { SkipPoWCheck = true }); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new Behavior(this)); ListenerTrace.Info("Fetching wallet rules..."); _Wallets = _Configuration.Indexer.CreateIndexerClient().GetAllWalletRules(); ListenerTrace.Info("Wallet rules fetched"); ListenerTrace.Info("Fetching wallet subscriptions..."); _Subscriptions = new SubscriptionCollection(_Configuration.GetSubscriptionsTable().Read()); ListenerTrace.Info("Subscriptions fetched"); _Group.Connect(); ListenerTrace.Info("Fetching transactions to broadcast..."); _Disposables.Add( Configuration .Topics .BroadcastedTransactions .CreateConsumer("listener", true) .EnsureSubscriptionExists() .OnMessage((tx, ctl) => { uint256 hash = null; var repo = Configuration.Indexer.CreateIndexerClient(); var rejects = Configuration.GetRejectTable(); try { hash = tx.Transaction.GetHash(); var indexedTx = repo.GetTransaction(hash); ListenerTrace.Info("Broadcasting " + hash); var reject = rejects.ReadOne(hash.ToString()); if (reject != null) { ListenerTrace.Info("Abort broadcasting of rejected"); return; } if (_Broadcasting.Count > 1000) { _Broadcasting.Clear(); } _Broadcasting.TryAdd(hash, tx.Transaction); if (indexedTx == null || !indexedTx.BlockIds.Any(id => Chain.Contains(id))) { var unused = SendMessageAsync(tx.Transaction); } var reschedule = new[] { TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), TimeSpan.FromHours(1), TimeSpan.FromHours(6), TimeSpan.FromHours(24), }; if (tx.Tried <= reschedule.Length - 1) { ctl.RescheduleIn(reschedule[tx.Tried]); tx.Tried++; } } catch (Exception ex) { if (!_Disposed) { LastException = ex; ListenerTrace.Error("Error for new broadcasted transaction " + hash, ex); throw; } } })); ListenerTrace.Info("Transactions to broadcast fetched"); _Disposables.Add(_Configuration .Topics .SubscriptionChanges .EnsureSubscriptionExists() .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessage(c => { using (_SubscriptionSlimLock.LockWrite()) { if (c.Added) { _Subscriptions.Add(c.Subscription); } else { _Subscriptions.Remove(c.Subscription.Id); } } })); _Disposables.Add(_Configuration .Topics .SendNotifications .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessageAsync((n, act) => { return(SendAsync(n, act).ContinueWith(t => { if (!_Disposed) { if (t.Exception != null) { LastException = t.Exception; } } })); }, new OnMessageOptions() { MaxConcurrentCalls = 1000, AutoComplete = true, AutoRenewTimeout = TimeSpan.Zero })); _Disposables.Add(Configuration .Topics .AddedAddresses .CreateConsumer("updater", true) .EnsureSubscriptionExists() .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessage(evt => { if (evt == null) { return; } ListenerTrace.Info("New wallet rule"); using (_WalletsSlimLock.LockWrite()) { foreach (var address in evt) { _Wallets.Add(address.CreateWalletRuleEntry()); } } })); }