< Summary - Combined Code Coverage

Information
Class: NLightning.Infrastructure.Node.Services.PeerService
Assembly: NLightning.Infrastructure
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Node/Services/PeerService.cs
Tag: 38_17925369700
Line coverage
0%
Covered lines: 0
Uncovered lines: 72
Coverable lines: 72
Total lines: 198
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 32
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PeerPubKey()100%210%
get_PreferredHost()100%210%
get_PreferredPort()100%210%
get_Features()100%210%
.ctor(...)100%210%
Disconnect()100%210%
SendMessageAsync(...)100%210%
HandleMessage(...)0%4260%
HandleException(...)100%210%
HandleDisconnection(...)0%620%
HandleInitialization(...)0%506220%
HandleChannelMessage(...)0%620%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Node/Services/PeerService.cs

#LineLine coverage
 1using System.Net;
 2using Microsoft.Extensions.Logging;
 3
 4namespace NLightning.Infrastructure.Node.Services;
 5
 6using Domain.Crypto.ValueObjects;
 7using Domain.Exceptions;
 8using Domain.Node.Events;
 9using Domain.Node.Interfaces;
 10using Domain.Node.Options;
 11using Domain.Protocol.Constants;
 12using Domain.Protocol.Interfaces;
 13using Domain.Protocol.Messages;
 14
 15// TODO: Eventually move this to the Application layer
 16/// <summary>
 17/// Service for peer communication
 18/// </summary>
 19public sealed class PeerService : IPeerService
 20{
 21    private readonly IPeerCommunicationService _peerCommunicationService;
 22    private readonly ILogger<PeerService> _logger;
 23
 24    private bool _isInitialized;
 25
 26    /// <inheritdoc/>
 27    public event EventHandler<PeerDisconnectedEventArgs>? OnDisconnect;
 28
 29    /// <inheritdoc/>
 30    public event EventHandler<ChannelMessageEventArgs>? OnChannelMessageReceived;
 31
 32    /// <inheritdoc/>
 033    public CompactPubKey PeerPubKey => _peerCommunicationService.PeerCompactPubKey;
 34
 035    public string? PreferredHost { get; private set; }
 036    public ushort? PreferredPort { get; private set; }
 37
 038    public FeatureOptions Features { get; private set; }
 39
 40    /// <summary>
 41    /// Initializes a new instance of the <see cref="PeerService"/> class.
 42    /// </summary>
 43    /// <param name="peerCommunicationService">The peer communication service</param>
 44    /// <param name="features">The feature options</param>
 45    /// <param name="logger">A logger</param>
 46    /// <param name="networkTimeout">Network timeout</param>
 047    public PeerService(IPeerCommunicationService peerCommunicationService, FeatureOptions features,
 048                       ILogger<PeerService> logger, TimeSpan networkTimeout)
 49    {
 050        _peerCommunicationService = peerCommunicationService;
 051        Features = features;
 052        _logger = logger;
 53
 54        // Set up event handlers
 055        _peerCommunicationService.MessageReceived += HandleMessage;
 056        _peerCommunicationService.ExceptionRaised += HandleException;
 057        _peerCommunicationService.DisconnectEvent += HandleDisconnection;
 58
 59        // Initialize communication
 60        try
 61        {
 062            _peerCommunicationService.InitializeAsync(networkTimeout).GetAwaiter().GetResult();
 063        }
 064        catch (Exception e)
 65        {
 066            _peerCommunicationService.MessageReceived -= HandleMessage;
 067            _peerCommunicationService.ExceptionRaised -= HandleException;
 068            _peerCommunicationService.DisconnectEvent -= HandleDisconnection;
 69
 070            throw new ErrorException("Error initializing peer communication", e);
 71        }
 072    }
 73
 74    /// <summary>
 75    /// Disconnects from the peer.
 76    /// </summary>
 77    public void Disconnect()
 78    {
 079        _logger.LogInformation("Disconnecting peer {peer}", PeerPubKey);
 080        _peerCommunicationService.Disconnect();
 081    }
 82
 83    public Task SendMessageAsync(IChannelMessage replyMessage)
 84    {
 085        return _peerCommunicationService.SendMessageAsync(replyMessage);
 86    }
 87
 88    /// <summary>
 89    /// Handles messages received from the peer.
 90    /// </summary>
 91    private void HandleMessage(object? sender, IMessage? message)
 92    {
 093        if (message is null)
 094            return;
 95
 096        if (!_isInitialized)
 97        {
 098            HandleInitialization(message);
 99        }
 0100        else if (message is IChannelMessage channelMessage)
 101        {
 102            // Handle channel-related messages
 0103            HandleChannelMessage(channelMessage);
 104        }
 0105    }
 106
 107    /// <summary>
 108    /// Handles exceptions raised by the communication service.
 109    /// </summary>
 110    private void HandleException(object? sender, Exception e)
 111    {
 0112        _logger.LogError(e, "Exception occurred with peer {peer}", PeerPubKey);
 0113    }
 114
 115    private void HandleDisconnection(object? sender, EventArgs e)
 116    {
 0117        _logger.LogTrace("Handling disconnection for peer {Peer}", PeerPubKey);
 0118        OnDisconnect?.Invoke(this, new PeerDisconnectedEventArgs(PeerPubKey));
 0119    }
 120
 121    /// <summary>
 122    /// Handles the initialization process when receiving the first message.
 123    /// </summary>
 124    private void HandleInitialization(IMessage message)
 125    {
 126        // Check if the first message is an init message
 0127        if (message.Type != MessageTypes.Init || message is not InitMessage initMessage)
 128        {
 0129            _logger.LogError("Failed to receive init message from peer {peer}", PeerPubKey);
 0130            Disconnect();
 0131            return;
 132        }
 133
 134        // Check if Features are compatible
 0135        if (!Features.GetNodeFeatures().IsCompatible(initMessage.Payload.FeatureSet, out var negotiatedFeatures)
 0136         || negotiatedFeatures is null)
 137        {
 0138            _logger.LogError("Peer {peer} is not compatible", PeerPubKey);
 0139            Disconnect();
 0140            return;
 141        }
 142
 143        // Check if ChainHash contained in networksTlv.ChainHashes exists in our ChainHashes
 0144        var networkChainHashes = initMessage.NetworksTlv?.ChainHashes;
 0145        if (networkChainHashes != null
 0146         && networkChainHashes.Any(chainHash => !Features.ChainHashes.Contains(chainHash)))
 147        {
 0148            _logger.LogError("Peer {peer} chain is not compatible", PeerPubKey);
 0149            Disconnect();
 0150            return;
 151        }
 152
 0153        if (initMessage.RemoteAddressTlv is not null)
 154        {
 0155            switch (initMessage.RemoteAddressTlv.AddressType)
 156            {
 157                case 1 or 2:
 158                    {
 0159                        if (!IPAddress.TryParse(initMessage.RemoteAddressTlv.Address, out var ipAddress))
 160                        {
 0161                            _logger.LogWarning("Peer {peer} has an invalid remote address: {address}",
 0162                                               PeerPubKey, initMessage.RemoteAddressTlv.Address);
 163                        }
 164                        else
 165                        {
 0166                            PreferredHost = ipAddress.ToString();
 0167                            PreferredPort = initMessage.RemoteAddressTlv.Port;
 168                        }
 169
 0170                        break;
 171                    }
 172                case 5:
 0173                    PreferredHost = initMessage.RemoteAddressTlv.Address;
 0174                    PreferredPort = initMessage.RemoteAddressTlv.Port;
 0175                    break;
 176                default:
 0177                    _logger.LogWarning("Peer {peer} has an unsupported remote address type: {addressType}",
 0178                                       PeerPubKey, initMessage.RemoteAddressTlv.AddressType);
 179                    break;
 180            }
 181        }
 182
 0183        Features = FeatureOptions.GetNodeOptions(negotiatedFeatures, initMessage.Extension);
 0184        _logger.LogTrace("Initialization from peer {peer} completed successfully", PeerPubKey);
 0185        _isInitialized = true;
 0186    }
 187
 188    /// <summary>
 189    /// Handles channel messages.
 190    /// </summary>
 191    private void HandleChannelMessage(IChannelMessage message)
 192    {
 0193        _logger.LogTrace("Received channel message ({messageType}) from peer {peer}",
 0194                         Enum.GetName(message.Type), PeerPubKey);
 195
 0196        OnChannelMessageReceived?.Invoke(this, new ChannelMessageEventArgs(message, PeerPubKey));
 0197    }
 198}