public bool IsSynchingCore(GetBlockchainInfoResponse blockchainInfo) { if (blockchainInfo.InitialBlockDownload == true) return true; if (blockchainInfo.MedianTime.HasValue && _Network.NBitcoinNetwork.NetworkType != NetworkType.Regtest) { var time = NBitcoin.Utils.UnixTimeToDateTime(blockchainInfo.MedianTime.Value); // 5 month diff? probably synching... if (DateTimeOffset.UtcNow - time > TimeSpan.FromDays(30 * 5)) { return true; } } return blockchainInfo.Headers - blockchainInfo.Blocks > 6; }
async Task <bool> StepAsync(CancellationToken token) { var oldState = State; switch (State) { case BitcoinDWaiterState.NotStarted: await RPCArgs.TestRPCAsync(_Network, _RPCWithTimeout, token); _OriginalRPC.Capabilities = _RPCWithTimeout.Capabilities; GetBlockchainInfoResponse blockchainInfo = null; try { blockchainInfo = await _RPCWithTimeout.GetBlockchainInfoAsyncEx(); if (_Network.CryptoCode == "BTC" && _Network.NBitcoinNetwork.NetworkType == NetworkType.Mainnet && !_BanListLoaded) { if (await LoadBanList()) { _BanListLoaded = true; } } if (blockchainInfo != null && _Network.NBitcoinNetwork.NetworkType == NetworkType.Regtest) { if (await WarmupBlockchain()) { blockchainInfo = await _RPCWithTimeout.GetBlockchainInfoAsyncEx(); } } } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failed to connect to RPC"); break; } if (IsSynchingCore(blockchainInfo)) { State = BitcoinDWaiterState.CoreSynching; } else { await ConnectToBitcoinD(token); State = BitcoinDWaiterState.NBXplorerSynching; } break; case BitcoinDWaiterState.CoreSynching: GetBlockchainInfoResponse blockchainInfo2 = null; try { blockchainInfo2 = await _RPCWithTimeout.GetBlockchainInfoAsyncEx(); } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failed to connect to RPC"); State = BitcoinDWaiterState.NotStarted; break; } if (!IsSynchingCore(blockchainInfo2)) { await ConnectToBitcoinD(token); State = BitcoinDWaiterState.NBXplorerSynching; } break; case BitcoinDWaiterState.NBXplorerSynching: var explorer = GetExplorerBehavior(); if (explorer == null) { State = BitcoinDWaiterState.NotStarted; } else if (!explorer.IsSynching()) { State = BitcoinDWaiterState.Ready; } break; case BitcoinDWaiterState.Ready: var explorer2 = GetExplorerBehavior(); if (explorer2 == null) { State = BitcoinDWaiterState.NotStarted; } else if (explorer2.IsSynching()) { State = BitcoinDWaiterState.NBXplorerSynching; } break; default: break; } var changed = oldState != State; if (changed) { if (oldState == BitcoinDWaiterState.NotStarted) { NetworkInfo = await _RPCWithTimeout.GetNetworkInfoAsync(); } _EventAggregator.Publish(new BitcoinDStateChangedEvent(_Network, oldState, State)); if (State == BitcoinDWaiterState.Ready) { await File.WriteAllTextAsync(RPCReadyFile, NBitcoin.Utils.DateTimeToUnixTime(DateTimeOffset.UtcNow).ToString()); } } if (State != BitcoinDWaiterState.Ready) { EnsureRPCReadyFileDeleted(); } return(changed); }
async Task <bool> StepAsync(CancellationToken token) { var oldState = State; switch (State) { case BitcoinDWaiterState.NotStarted: await RPCArgs.TestRPCAsync(_Network, _RPC, token); GetBlockchainInfoResponse blockchainInfo = null; try { blockchainInfo = await _RPC.GetBlockchainInfoAsyncEx(); if (blockchainInfo != null && _Network.NBitcoinNetwork.NetworkType == NetworkType.Regtest) { if (await WarmupBlockchain()) { blockchainInfo = await _RPC.GetBlockchainInfoAsyncEx(); } } } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failed to connect to RPC"); break; } if (IsSynchingCore(blockchainInfo)) { State = BitcoinDWaiterState.CoreSynching; } else { await ConnectToBitcoinD(token); State = BitcoinDWaiterState.NBXplorerSynching; } break; case BitcoinDWaiterState.CoreSynching: GetBlockchainInfoResponse blockchainInfo2 = null; try { blockchainInfo2 = await _RPC.GetBlockchainInfoAsyncEx(); } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failed to connect to RPC"); State = BitcoinDWaiterState.NotStarted; break; } if (!IsSynchingCore(blockchainInfo2)) { await ConnectToBitcoinD(token); State = BitcoinDWaiterState.NBXplorerSynching; } break; case BitcoinDWaiterState.NBXplorerSynching: var explorer = _Group?.ConnectedNodes.SelectMany(n => n.Behaviors.OfType <ExplorerBehavior>()).FirstOrDefault(); if (explorer == null) { GetBlockchainInfoResponse blockchainInfo3 = null; try { blockchainInfo3 = await _RPC.GetBlockchainInfoAsyncEx(); } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failed to connect to RPC"); State = BitcoinDWaiterState.NotStarted; break; } if (IsSynchingCore(blockchainInfo3)) { State = BitcoinDWaiterState.CoreSynching; } } else if (!explorer.IsSynching()) { State = BitcoinDWaiterState.Ready; } break; case BitcoinDWaiterState.Ready: var explorer2 = _Group?.ConnectedNodes.SelectMany(n => n.Behaviors.OfType <ExplorerBehavior>()).FirstOrDefault(); if (explorer2 == null) { State = BitcoinDWaiterState.NotStarted; } else if (explorer2.IsSynching()) { State = BitcoinDWaiterState.NBXplorerSynching; } break; default: break; } var changed = oldState != State; if (changed) { _EventAggregator.Publish(new BitcoinDStateChangedEvent(_Network, oldState, State)); } return(changed); }
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; } }