public async Task CanConnectWithDifferentModesAsync() { var connector = new BestEffortEndpointConnector(6); var nodeConnectionParameters = new NodeConnectionParameters(); nodeConnectionParameters.TemplateBehaviors.Add(new SocksSettingsBehavior(new IPEndPoint(IPAddress.Loopback, 8090), onlyForOnionHosts: true, networkCredential: null, streamIsolation: true)); using var nodes = new NodesGroup(Network.TestNet, nodeConnectionParameters); async Task ConnectAsync(EndPoint endpoint) { using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); await connector.ConnectSocket(socket, endpoint, nodeConnectionParameters, CancellationToken.None); } Exception ex; // Try to connect to a non-onion address. ex = await Assert.ThrowsAsync <SocketException>( async() => await ConnectAsync(new IPEndPoint(IPAddress.Loopback, 180))); Assert.Contains("refused", ex.Message); Assert.False(connector.State.AllowOnlyTorEndpoints); // Try to connect to an onion address (it has to fail because there is no real socks proxy listening). ex = await Assert.ThrowsAnyAsync <SocketException>( async() => await ConnectAsync(new DnsEndPoint("nec4kn4ghql7p7an.onion", 180))); Assert.Contains("refused", ex.Message); Assert.False(connector.State.AllowOnlyTorEndpoints); // Simulate we lost connection. connector.State.ConnectedNodesCount = 10; ex = await Assert.ThrowsAsync <SocketException>( async() => await ConnectAsync(new IPEndPoint(IPAddress.Loopback, 180))); Assert.Contains("refused", ex.Message); Assert.True(connector.State.AllowOnlyTorEndpoints); ex = await Assert.ThrowsAnyAsync <SocketException>( async() => await ConnectAsync(new DnsEndPoint("nec4kn4ghql7p7an.onion", 180))); Assert.Contains("refused", ex.Message); Assert.True(connector.State.AllowOnlyTorEndpoints); // Simulate we lost connection. connector.State.ConnectedNodesCount = 0; ex = await Assert.ThrowsAsync <SocketException>( async() => await ConnectAsync(new IPEndPoint(IPAddress.Loopback, 180))); Assert.Contains("refused", ex.Message); Assert.False(connector.State.AllowOnlyTorEndpoints); // Try to connect to an onion address (it has to fail because there is no real socks proxy listening). ex = await Assert.ThrowsAnyAsync <SocketException>( async() => await ConnectAsync(new DnsEndPoint("nec4kn4ghql7p7an.onion", 180))); Assert.Contains("refused", ex.Message); Assert.False(connector.State.AllowOnlyTorEndpoints); // Enough peers with recent connection. connector.State.ConnectedNodesCount = 10; ex = await Assert.ThrowsAnyAsync <SocketException>( async() => await ConnectAsync(new DnsEndPoint("nec4kn4ghql7p7an.onion", 180))); Assert.Contains("refused", ex.Message); Assert.True(connector.State.AllowOnlyTorEndpoints); }
public P2pNetwork(Network network, EndPoint fullnodeP2pEndPoint, EndPoint?torSocks5EndPoint, string workDir, BitcoinStore bitcoinStore) { Network = network; FullnodeP2PEndPoint = fullnodeP2pEndPoint; TorSocks5EndPoint = torSocks5EndPoint; WorkDir = workDir; BitcoinStore = bitcoinStore; var userAgent = Constants.UserAgents.RandomElement(); var connectionParameters = new NodeConnectionParameters { UserAgent = userAgent }; connectionParameters.TemplateBehaviors.Add(BitcoinStore.CreateUntrustedP2pBehavior()); AddressManagerFilePath = Path.Combine(WorkDir, $"AddressManager{Network}.dat"); var needsToDiscoverPeers = true; if (Network == Network.RegTest) { AddressManager = new AddressManager(); Logger.LogInfo($"Fake {nameof(AddressManager)} is initialized on the {Network.RegTest}."); } else { try { AddressManager = AddressManager.LoadPeerFile(AddressManagerFilePath); // Most of the times we do not need to discover new peers. Instead, we can connect to // some of those that we already discovered in the past. In this case we assume that // discovering new peers could be necessary if our address manager has less // than 500 addresses. 500 addresses could be okay because previously we tried with // 200 and only one user reported he/she was not able to connect (there could be many others, // of course). // On the other side, increasing this number forces users that do not need to discover more peers // to spend resources (CPU/bandwidth) to discover new peers. needsToDiscoverPeers = TorSocks5EndPoint is not null || AddressManager.Count < 500; Logger.LogInfo($"Loaded {nameof(AddressManager)} from `{AddressManagerFilePath}`."); } catch (DirectoryNotFoundException ex) { Logger.LogInfo($"{nameof(AddressManager)} did not exist at `{AddressManagerFilePath}`. Initializing new one."); Logger.LogTrace(ex); AddressManager = new AddressManager(); } catch (FileNotFoundException ex) { Logger.LogInfo($"{nameof(AddressManager)} did not exist at `{AddressManagerFilePath}`. Initializing new one."); Logger.LogTrace(ex); AddressManager = new AddressManager(); } catch (OverflowException ex) { // https://github.com/zkSNACKs/WalletWasabi/issues/712 Logger.LogInfo($"{nameof(AddressManager)} has thrown `{nameof(OverflowException)}`. Attempting to autocorrect."); File.Delete(AddressManagerFilePath); Logger.LogTrace(ex); AddressManager = new AddressManager(); Logger.LogInfo($"{nameof(AddressManager)} autocorrection is successful."); } catch (FormatException ex) { // https://github.com/zkSNACKs/WalletWasabi/issues/880 Logger.LogInfo($"{nameof(AddressManager)} has thrown `{nameof(FormatException)}`. Attempting to autocorrect."); File.Delete(AddressManagerFilePath); Logger.LogTrace(ex); AddressManager = new AddressManager(); Logger.LogInfo($"{nameof(AddressManager)} autocorrection is successful."); } } var addressManagerBehavior = new AddressManagerBehavior(AddressManager) { Mode = needsToDiscoverPeers ? AddressManagerBehaviorMode.Discover : AddressManagerBehaviorMode.None }; connectionParameters.TemplateBehaviors.Add(addressManagerBehavior); if (Network == Network.RegTest) { Nodes = new NodesGroup(Network, requirements: Constants.NodeRequirements); } else { var maximumNodeConnection = 12; var bestEffortEndpointConnector = new BestEffortEndpointConnector(maximumNodeConnection / 2); connectionParameters.EndpointConnector = bestEffortEndpointConnector; if (TorSocks5EndPoint is not null) { connectionParameters.TemplateBehaviors.Add(new SocksSettingsBehavior(TorSocks5EndPoint, onlyForOnionHosts: false, networkCredential: null, streamIsolation: true)); } var nodes = new NodesGroup(Network, connectionParameters, requirements: Constants.NodeRequirements); nodes.ConnectedNodes.Added += ConnectedNodes_OnAddedOrRemoved; nodes.ConnectedNodes.Removed += ConnectedNodes_OnAddedOrRemoved; nodes.MaximumNodeConnection = maximumNodeConnection; Nodes = nodes; } }