private async Task ConnectToBitcoinD(CancellationToken cancellation, GetBlockchainInfoResponse blockchainInfo) { var node = GetHandshakedNode(); if (node != null) { return; } try { EnsureNodeDisposed(); _Chain.ResetToGenesis(); _Chain.SetCapacity((int)(blockchainInfo.Headers * 1.1)); if (_Configuration.CacheChain) { LoadChainFromCache(); if (!await HasBlock(_OriginalRPC, _Chain.Tip)) { Logs.Configuration.LogInformation($"{_Network.CryptoCode}: The cached chain contains a tip unknown to the node, dropping the cache..."); _Chain.ResetToGenesis(); } } var heightBefore = _Chain.Height; using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { timeout.CancelAfter(_Network.ChainLoadingTimeout); Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Trying to connect via the P2P protocol to trusted node ({_ChainConfiguration.NodeEndpoint.ToEndpointString()})..."); var userAgent = "NBXplorer-" + RandomUtils.GetInt64(); bool handshaked = false; bool connected = false; bool chainLoaded = false; using (var handshakeTimeout = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { try { handshakeTimeout.CancelAfter(TimeSpan.FromSeconds(10)); node = await Node.ConnectAsync(_Network.NBitcoinNetwork, _ChainConfiguration.NodeEndpoint, new NodeConnectionParameters() { UserAgent = userAgent, ConnectCancellation = handshakeTimeout.Token, IsRelay = true }); connected = true; Logs.Explorer.LogInformation($"{Network.CryptoCode}: TCP Connection succeed, handshaking..."); node.VersionHandshake(handshakeTimeout.Token); handshaked = true; Logs.Explorer.LogInformation($"{Network.CryptoCode}: Handshaked"); var loadChainTimeout = _Network.NBitcoinNetwork.ChainName == ChainName.Regtest ? TimeSpan.FromSeconds(5) : _Network.ChainCacheLoadingTimeout; if (_Chain.Height < 5) { loadChainTimeout = TimeSpan.FromDays(7); // unlimited } Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Loading chain from node"); try { using (var cts1 = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { cts1.CancelAfter(loadChainTimeout); Logs.Explorer.LogInformation($"{Network.CryptoCode}: Loading chain..."); node.SynchronizeSlimChain(_Chain, cancellationToken: cts1.Token); } } catch when(!cancellation.IsCancellationRequested) // Timeout happens with SynchronizeChain, if so, throw away the cached chain { Logs.Explorer.LogInformation($"{Network.CryptoCode}: Failed to load chain before timeout, let's try again without the chain cache..."); _Chain.ResetToGenesis(); node.SynchronizeSlimChain(_Chain, cancellationToken: cancellation); } Logs.Explorer.LogInformation($"{Network.CryptoCode}: Chain loaded"); chainLoaded = true; var peer = (await _OriginalRPC.GetPeersInfoAsync()) .FirstOrDefault(p => p.SubVersion == userAgent); if (IsWhitelisted(peer)) { Logs.Explorer.LogInformation($"{Network.CryptoCode}: NBXplorer is correctly whitelisted by the node"); } else { var addressStr = peer.Address is IPEndPoint end?end.Address.ToString() : peer.Address?.ToString(); Logs.Explorer.LogWarning($"{Network.CryptoCode}: Your NBXplorer server is not whitelisted by your node," + $" you should add \"whitelist={addressStr}\" to the configuration file of your node. (Or use whitebind)"); } } catch { if (!connected) { Logs.Explorer.LogWarning($"{Network.CryptoCode}: NBXplorer failed to connect to the node via P2P ({_ChainConfiguration.NodeEndpoint.ToEndpointString()}).{Environment.NewLine}" + $"It may come from: A firewall blocking the traffic, incorrect IP or port, or your node may not have an available connection slot. {Environment.NewLine}" + $"To make sure your node have an available connection slot, use \"whitebind\" or \"whitelist\" in your node configuration. (typically whitelist=127.0.0.1 if NBXplorer and the node are on the same machine.){Environment.NewLine}"); } else if (!handshaked) { Logs.Explorer.LogWarning($"{Network.CryptoCode}: NBXplorer connected to the remote node but failed to handhsake via P2P.{Environment.NewLine}" + $"Your node may not have an available connection slot, or you may try to connect to the wrong node. (ie, trying to connect to a LTC node on the BTC configuration).{Environment.NewLine}" + $"To make sure your node have an available connection slot, use \"whitebind\" or \"whitelist\" in your node configuration. (typically whitelist=127.0.0.1 if NBXplorer and the node are on the same machine.){Environment.NewLine}"); } else if (!chainLoaded) { Logs.Explorer.LogWarning($"{Network.CryptoCode}: NBXplorer connected and handshaked the remote node but failed to load the chain of header.{Environment.NewLine}" + $"Your connection may be throttled, or you may try to connect to the wrong node. (ie, trying to connect to a LTC node on the BTC configuration).{Environment.NewLine}"); } throw; } } } Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Height: " + _Chain.Height); if (_Configuration.CacheChain && heightBefore != _Chain.Height) { SaveChainInCache(); } GC.Collect(); node.Behaviors.Add(new SlimChainBehavior(_Chain)); var explorer = (ExplorerBehavior)_ExplorerPrototype.Clone(); node.Behaviors.Add(explorer); node.StateChanged += Node_StateChanged; _Node = node; } catch { EnsureNodeDisposed(node ?? _Node); throw; } }
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(); } } }
private async Task ConnectToBitcoinD(CancellationToken cancellation) { var node = GetHandshakedNode(); if (node != null) { return; } try { EnsureNodeDisposed(); _Chain.ResetToGenesis(); if (_Configuration.CacheChain) { LoadChainFromCache(); if (!await HasBlock(_RPCWithTimeout, _Chain.Tip)) { Logs.Configuration.LogInformation($"{_Network.CryptoCode}: The cached chain contains a tip unknown to the node, dropping the cache..."); _Chain.ResetToGenesis(); } } var heightBefore = _Chain.Height; using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { timeout.CancelAfter(_Network.ChainLoadingTimeout); try { Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Trying to connect via the P2P protocol to trusted node ({_ChainConfiguration.NodeEndpoint.ToEndpointString()})..."); var userAgent = "NBXplorer-" + RandomUtils.GetInt64(); bool handshaked = false; using (var handshakeTimeout = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { try { handshakeTimeout.CancelAfter(TimeSpan.FromSeconds(10)); node = await Node.ConnectAsync(_Network.NBitcoinNetwork, _ChainConfiguration.NodeEndpoint, new NodeConnectionParameters() { UserAgent = userAgent, ConnectCancellation = handshakeTimeout.Token, IsRelay = true }); try { Logs.Explorer.LogInformation($"{Network.CryptoCode}: TCP Connection succeed, handshaking..."); node.VersionHandshake(handshakeTimeout.Token); Logs.Explorer.LogInformation($"{Network.CryptoCode}: Handshaked"); } catch (OperationCanceledException) when(handshakeTimeout.IsCancellationRequested) { Logs.Explorer.LogWarning($"{Network.CryptoCode}: NBXplorer could not complete the handshake with the remote node. This is probably because NBXplorer is not whitelisted by your node.{Environment.NewLine}" + $"You can use \"whitebind\" or \"whitelist\" in your node configuration. (typically whitelist=127.0.0.1 if NBXplorer and the node are on the same machine.){Environment.NewLine}" + $"This issue can also happen because NBXplorer do not manage to connect to the P2P port of your node at all."); throw; } handshaked = true; var loadChainTimeout = _Network.NBitcoinNetwork.NetworkType == NetworkType.Regtest ? TimeSpan.FromSeconds(5) : TimeSpan.FromSeconds(15); if (_Chain.Height < 5) { loadChainTimeout = TimeSpan.FromDays(7); // unlimited } Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Loading chain from node"); try { using (var cts1 = CancellationTokenSource.CreateLinkedTokenSource(cancellation)) { cts1.CancelAfter(loadChainTimeout); Logs.Explorer.LogInformation($"{Network.CryptoCode}: Loading chain..."); node.SynchronizeSlimChain(_Chain, cancellationToken: cts1.Token); } } catch when(!cancellation.IsCancellationRequested) // Timeout happens with SynchronizeChain, if so, throw away the cached chain { Logs.Explorer.LogInformation($"{Network.CryptoCode}: Failed to load chain before timeout, let's try again without the chain cache..."); _Chain.ResetToGenesis(); node.SynchronizeSlimChain(_Chain, cancellationToken: cancellation); } Logs.Explorer.LogInformation($"{Network.CryptoCode}: Chain loaded"); var peer = (await _RPCWithTimeout.GetPeersInfoAsync()) .FirstOrDefault(p => p.SubVersion == userAgent); if (peer != null && !peer.IsWhiteListed) { var addressStr = peer.Address?.Address?.ToString(); if (addressStr == null) { addressStr = peer.AddressString; var portDelimiter = addressStr.LastIndexOf(':'); if (portDelimiter != -1) { addressStr = addressStr.Substring(0, portDelimiter); } } Logs.Explorer.LogWarning($"{Network.CryptoCode}: Your NBXplorer server is not whitelisted by your node," + $" you should add \"whitelist={addressStr}\" to the configuration file of your node. (Or use whitebind)"); } if (peer != null && peer.IsWhiteListed) { Logs.Explorer.LogInformation($"{Network.CryptoCode}: NBXplorer is correctly whitelisted by the node"); } } catch (OperationCanceledException) when(!handshaked && handshakeTimeout.IsCancellationRequested) { Logs.Explorer.LogWarning($"{Network.CryptoCode}: The initial hanshake failed, your NBXplorer server might not be whitelisted by your node," + $" if your bitcoin node is on the same machine as NBXplorer, you should add \"whitelist=127.0.0.1\" to the configuration file of your node. (Or use whitebind)"); throw; } } Logs.Configuration.LogInformation($"{_Network.CryptoCode}: Height: " + _Chain.Height); } catch (Exception ex) when(!cancellation.IsCancellationRequested) { throw new OperationCanceledException("Loading the chain from the node timed out", ex, timeout.Token); } } if (_Configuration.CacheChain && heightBefore != _Chain.Height) { SaveChainInCache(); } GC.Collect(); node.Behaviors.Add(new SlimChainBehavior(_Chain)); var explorer = (ExplorerBehavior)_ExplorerPrototype.Clone(); node.Behaviors.Add(explorer); node.StateChanged += Node_StateChanged; _Node = node; await explorer.Init(); } catch { EnsureNodeDisposed(node ?? _Node); throw; } }