예제 #1
0
        public bool DisconnectOnInvalid(string protocol, ISession session, ProtocolInitializedEventArgs eventArgs)
        {
            switch (protocol)
            {
            case Protocol.P2P:
                P2PProtocolInitializedEventArgs args = (P2PProtocolInitializedEventArgs)eventArgs;
                if (!ValidateP2PVersion(args.P2PVersion))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, incorrect P2PVersion: {args.P2PVersion}");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.P2PVersion);
                    Disconnect(session, DisconnectReason.IncompatibleP2PVersion, $"p2p.{args.P2PVersion}");
                    if (session.Node.IsStatic && _logger.IsWarn)
                    {
                        _logger.Warn($"Disconnected an invalid static node: {session.Node.Host}:{session.Node.Port}, reason: {DisconnectReason.IncompatibleP2PVersion}");
                    }
                    return(false);
                }

                if (!ValidateCapabilities(args.Capabilities))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, no Eth62 capability, supported capabilities: [{string.Join(",", args.Capabilities.Select(x => $"{x.ProtocolCode}v{x.Version}"))}]");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.Capabilities);
                    Disconnect(session, DisconnectReason.UselessPeer, "capabilities");
                    if (session.Node.IsStatic && _logger.IsWarn)
                    {
                        _logger.Warn($"Disconnected an invalid static node: {session.Node.Host}:{session.Node.Port}, reason: {DisconnectReason.UselessPeer} (capabilities)");
                    }
                    return(false);
                }

                break;

            case Protocol.Eth:
            case Protocol.Les:
                SyncPeerProtocolInitializedEventArgs syncPeerArgs = (SyncPeerProtocolInitializedEventArgs)eventArgs;
                if (!ValidateChainId(syncPeerArgs.ChainId))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, different chainId: {ChainId.GetChainName((int) syncPeerArgs.ChainId)}, our chainId: {ChainId.GetChainName(_blockTree.ChainId)}");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.ChainId);
                    Disconnect(session, DisconnectReason.UselessPeer, $"invalid chain id - {syncPeerArgs.ChainId}");
                    if (session.Node.IsStatic && _logger.IsWarn)
                    {
                        _logger.Warn($"Disconnected an invalid static node: {session.Node.Host}:{session.Node.Port}, reason: {DisconnectReason.UselessPeer} (invalid chain id - {syncPeerArgs.ChainId})");
                    }
                    return(false);
                }

                if (syncPeerArgs.GenesisHash != _blockTree.Genesis.Hash)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, different genesis hash: {syncPeerArgs.GenesisHash}, our: {_blockTree.Genesis.Hash}");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.DifferentGenesis);
                    Disconnect(session, DisconnectReason.BreachOfProtocol, "invalid genesis");
                    if (session.Node.IsStatic && _logger.IsWarn)
                    {
                        _logger.Warn($"Disconnected an invalid static node: {session.Node.Host}:{session.Node.Port}, reason: {DisconnectReason.BreachOfProtocol} (invalid genesis)");
                    }
                    return(false);
                }

                break;
            }

            return(true);
        }
예제 #2
0
        public void DumpStats(IReadOnlyCollection <INodeStats> nodes, bool logEventDetails)
        {
            var eventTypes = Enum.GetValues(typeof(NodeStatsEventType)).OfType <NodeStatsEventType>().Where(x => !x.ToString().Contains("Discovery"))
                             .OrderBy(x => x).ToArray();
            var eventStats = eventTypes.Select(x => new
            {
                EventType = x.ToString(),
                Count     = nodes.Count(y => y.DidEventHappen(x))
            }).ToArray();

            var chains = nodes.Where(x => x.EthNodeDetails != null).GroupBy(x => x.EthNodeDetails.ChainId).Select(
                x => new { ChainName = ChainId.GetChainName((int)x.Key), Count = x.Count() }).ToArray();
            var clients = nodes.Where(x => x.P2PNodeDetails != null).Select(x => x.P2PNodeDetails.ClientId).GroupBy(x => x).Select(
                x => new { ClientId = x.Key, Count = x.Count() }).ToArray();
            var remoteDisconnect = nodes.Count(x => x.EventHistory.Any(y => y.DisconnectDetails != null && y.DisconnectDetails.DisconnectType == DisconnectType.Remote));

            var sb = new StringBuilder();

            sb.AppendLine($"Session stats | {DateTime.Now.ToString(DetailedTimeDateFormat)} | Peers: {nodes.Count}");
            sb.AppendLine($"Peers count with each EVENT:{Environment.NewLine}" +
                          $"{string.Join(Environment.NewLine, eventStats.Select(x => $"{x.EventType.ToString()}:{x.Count}"))}{Environment.NewLine}" +
                          $"Remote disconnect: {remoteDisconnect}{Environment.NewLine}{Environment.NewLine}" +
                          $"CHAINS: {Environment.NewLine}" +
                          $"{string.Join(Environment.NewLine, chains.Select(x => $"{x.ChainName}:{x.Count}"))}{Environment.NewLine}{Environment.NewLine}" +
                          $"CLIENTS:{Environment.NewLine}" +
                          $"{string.Join(Environment.NewLine, clients.Select(x => $"{x.ClientId}:{x.Count}"))}{Environment.NewLine}");

            var peersWithLatencyStats = nodes.Where(x => x.LatencyHistory.Any()).ToArray();

            if (peersWithLatencyStats.Any())
            {
                var latencyLog = GetLatencyComparisonLog(peersWithLatencyStats);
                sb.AppendLine(latencyLog);
            }

            if (_statsConfig.CaptureNodeStatsEventHistory && logEventDetails)
            {
                sb.AppendLine($"All peers ({nodes.Count}):");
                sb.AppendLine($"{string.Join(Environment.NewLine, nodes.Select(GetNodeLog))}{Environment.NewLine}");

                var peersWithEvents = nodes.Where(x => x.EventHistory.Any(y => y.EventType != NodeStatsEventType.NodeDiscovered)).ToArray();
                sb.AppendLine($"Logging {peersWithEvents.Length} peers log event histories");
                foreach (var peer in peersWithEvents)
                {
                    LogPeerEventHistory(peer);
                }
            }
            else
            {
                sb.AppendLine($"Detailed session log disabled, CaptureNodeStatsEventHistory: {_statsConfig.CaptureNodeStatsEventHistory}, logEventDetails: {logEventDetails}");
            }

            sb.AppendLine();
            var generalFilePath = Path.Combine(_eventLogsDirectoryPath, "generalStats.log");
            var content         = sb.ToString();

            File.AppendAllText(generalFilePath, content);
            if (_logger.IsTrace)
            {
                _logger.Trace(content);
            }
        }
예제 #3
0
        public bool DisconnectOnInvalid(string protocol, ISession session, ProtocolInitializedEventArgs eventArgs)
        {
            switch (protocol)
            {
            case Protocol.P2P:
                var args = (P2PProtocolInitializedEventArgs)eventArgs;
                if (!ValidateP2PVersion(args.P2PVersion))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, incorrect P2PVersion: {args.P2PVersion}");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.P2PVersion);
                    Disconnect(session, DisconnectReason.IncompatibleP2PVersion);
                    return(false);
                }

                if (!ValidateCapabilities(args.Capabilities))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, no Eth62 capability, supported capabilities: [{string.Join(",", args.Capabilities.Select(x => $"{x.ProtocolCode}v{x.Version}"))}]");
                    }
                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.Capabilities);
                    Disconnect(session, DisconnectReason.UselessPeer);
                    return(false);
                }

                break;

            case Protocol.Eth:
                var ethArgs = (EthProtocolInitializedEventArgs)eventArgs;
                if (!ValidateChainId(ethArgs.ChainId))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, different chainId: {ChainId.GetChainName((int) ethArgs.ChainId)}, our chainId: {ChainId.GetChainName(_blockTree.ChainId)}");
                    }

                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.ChainId);
                    Disconnect(session, DisconnectReason.UselessPeer);
                    return(false);
                }

                if (ethArgs.GenesisHash != _blockTree.Genesis.Hash)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Initiating disconnect with peer: {session.RemoteNodeId}, different genesis hash: {ethArgs.GenesisHash}, our: {_blockTree.Genesis.Hash}");
                    }

                    _nodeStatsManager.ReportFailedValidation(session.Node, CompatibilityValidationType.DifferentGenesis);
                    Disconnect(session, DisconnectReason.BreachOfProtocol);
                    return(false);
                }

                break;
            }

            return(true);
        }
예제 #4
0
        private StringBuilder BuildNodeStats(INodeStats nodeStats)
        {
            var sb = new StringBuilder();

            sb.AppendLine();
            sb.AppendLine($"NodeEventHistory | {DateTime.Now.ToString(DetailedTimeDateFormat)}, {GetNodeLog(nodeStats)}");

            if (nodeStats.P2PNodeDetails != null)
            {
                sb.AppendLine($"P2P details: ClientId: {nodeStats.P2PNodeDetails.ClientId}, P2PVersion: {nodeStats.P2PNodeDetails.P2PVersion}, Capabilities: {GetCapabilities(nodeStats.P2PNodeDetails)}");
            }

            if (nodeStats.EthNodeDetails != null)
            {
                sb.AppendLine($"Eth62 details: ChainId: {ChainId.GetChainName((int) nodeStats.EthNodeDetails.ChainId)}, TotalDifficulty: {nodeStats.EthNodeDetails.TotalDifficulty}");
            }

            foreach (var statsEvent in nodeStats.EventHistory.OrderBy(x => x.EventDate).ToArray())
            {
                sb.Append($"{statsEvent.EventDate.ToString(DetailedTimeDateFormat)} | {statsEvent.EventType}");
                if (statsEvent.ConnectionDirection.HasValue)
                {
                    sb.Append($" | {statsEvent.ConnectionDirection.Value.ToString()}");
                }

                if (statsEvent.P2PNodeDetails != null)
                {
                    sb.Append($" | {statsEvent.P2PNodeDetails.ClientId} | v{statsEvent.P2PNodeDetails.P2PVersion} | {GetCapabilities(statsEvent.P2PNodeDetails)}");
                }

                if (statsEvent.EthNodeDetails != null)
                {
                    sb.Append($" | {ChainId.GetChainName((int) statsEvent.EthNodeDetails.ChainId)} | TotalDifficulty:{statsEvent.EthNodeDetails.TotalDifficulty}");
                }

                if (statsEvent.DisconnectDetails != null)
                {
                    sb.Append($" | {statsEvent.DisconnectDetails.DisconnectReason.ToString()} | {statsEvent.DisconnectDetails.DisconnectType.ToString()}");
                }

                if (statsEvent.SyncNodeDetails != null && (statsEvent.SyncNodeDetails.NodeBestBlockNumber.HasValue || statsEvent.SyncNodeDetails.OurBestBlockNumber.HasValue))
                {
                    sb.Append($" | NodeBestBlockNumber: {statsEvent.SyncNodeDetails.NodeBestBlockNumber} | OurBestBlockNumber: {statsEvent.SyncNodeDetails.OurBestBlockNumber}");
                }

                sb.AppendLine();
            }

            sb.AppendLine();
            sb.AppendLine("Latency averages:");
            var averageLatencies = GetAverageLatencies(nodeStats);

            foreach (var latency in averageLatencies.Where(x => x.Value.HasValue))
            {
                sb.AppendLine($"{latency.Key.ToString()} = {latency.Value}");
            }

            if (nodeStats.LatencyHistory.Any())
            {
                sb.AppendLine("Latency events:");
                foreach (var statsEvent in nodeStats.LatencyHistory.OrderBy(x => x.StatType).ThenBy(x => x.CaptureTime).ToArray())
                {
                    sb.AppendLine($"{statsEvent.StatType.ToString()} | {statsEvent.CaptureTime.ToString(DetailedTimeDateFormat)} | {statsEvent.Latency}");
                }
            }

            return(sb);
        }
예제 #5
0
        private async Task <bool> ValidateProtocol(string protocol, Peer peer, ProtocolInitializedEventArgs eventArgs)
        {
            //TODO add validation for clientId - e.g. get only ethereumJ clients
            switch (protocol)
            {
            case Protocol.P2P:
                var args = (P2PProtocolInitializedEventArgs)eventArgs;
                if (args.P2PVersion < 4 || args.P2PVersion > 5)
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Initiating disconnect with peer: {peer.Node.Id}, incorrect P2PVersion: {args.P2PVersion}");
                    }
                    await peer.Session.InitiateDisconnectAsync(DisconnectReason.IncompatibleP2PVersion);

                    return(false);
                }

                if (!args.Capabilities.Any(x => x.ProtocolCode == Protocol.Eth && x.Version == 62))
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Initiating disconnect with peer: {peer.Node.Id}, no Eth62 capability, supported capabilities: [{string.Join(",", args.Capabilities.Select(x => $"{x.ProtocolCode}v{x.Version}"))}]");
                    }
                    //TODO confirm disconnect reason
                    await peer.Session.InitiateDisconnectAsync(DisconnectReason.Other);

                    return(false);
                }

                //if (args.ClientId.Contains("Geth") || args.ClientId.Contains("Parity") || args.ClientId.Contains("Gnekonium"))
                //{
                //    if (_logger.IsInfoEnabled)
                //    {
                //        _logger.Info($"Initiating disconnect, rejecting client: {args.ClientId}, id: {peer.Node.Id}");
                //    }
                //    await peer.Session.InitiateDisconnectAsync(DisconnectReason.Other);
                //    return false;
                //}
                break;

            case Protocol.Eth:
                var ethArgs = (Eth62ProtocolInitializedEventArgs)eventArgs;
                if (ethArgs.ChainId != _synchronizationManager.ChainId)
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Initiating disconnect with peer: {peer.Node.Id}, different chainId: {ChainId.GetChainName((int)ethArgs.ChainId)}, our chainId: {ChainId.GetChainName(_synchronizationManager.ChainId)}");
                    }
                    //TODO confirm disconnect reason
                    await peer.Session.InitiateDisconnectAsync(DisconnectReason.Other);

                    return(false);
                }
                if (ethArgs.GenesisHash != _synchronizationManager.Genesis.Hash)
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Initiating disconnect with peer: {peer.Node.Id}, different genesis hash: {ethArgs.GenesisHash}, our: {_synchronizationManager.Genesis.Hash}");
                    }
                    //TODO confirm disconnect reason
                    await peer.Session.InitiateDisconnectAsync(DisconnectReason.Other);

                    return(false);
                }
                break;
            }
            return(true);
        }