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); }
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); } }
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); }
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); }
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); }