Esempio n. 1
0
		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;
		}
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        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;
            }
        }