< 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: 36_15743069263
Line coverage
0%
Covered lines: 0
Uncovered lines: 66
Coverable lines: 66
Total lines: 186
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.Node.Events;
 8using Domain.Node.Interfaces;
 9using Domain.Node.Options;
 10using Domain.Protocol.Constants;
 11using Domain.Protocol.Interfaces;
 12using Domain.Protocol.Messages;
 13
 14// TODO: Eventually move this to the Application layer
 15/// <summary>
 16/// Service for peer communication
 17/// </summary>
 18public sealed class PeerService : IPeerService
 19{
 20    private readonly IPeerCommunicationService _peerCommunicationService;
 21    private readonly ILogger<PeerService> _logger;
 22
 23    private bool _isInitialized;
 24
 25    /// <inheritdoc/>
 26    public event EventHandler<PeerDisconnectedEventArgs>? OnDisconnect;
 27
 28    /// <inheritdoc/>
 29    public event EventHandler<ChannelMessageEventArgs>? OnChannelMessageReceived;
 30
 31    /// <inheritdoc/>
 032    public CompactPubKey PeerPubKey => _peerCommunicationService.PeerCompactPubKey;
 33
 034    public string? PreferredHost { get; private set; }
 035    public ushort? PreferredPort { get; private set; }
 36
 037    public FeatureOptions Features { get; private set; }
 38
 39    /// <summary>
 40    /// Initializes a new instance of the <see cref="PeerService"/> class.
 41    /// </summary>
 42    /// <param name="peerCommunicationService">The peer communication service</param>
 43    /// <param name="features">The feature options</param>
 44    /// <param name="logger">A logger</param>
 45    /// <param name="networkTimeout">Network timeout</param>
 046    public PeerService(IPeerCommunicationService peerCommunicationService, FeatureOptions features,
 047                       ILogger<PeerService> logger, TimeSpan networkTimeout)
 48    {
 049        _peerCommunicationService = peerCommunicationService;
 050        Features = features;
 051        _logger = logger;
 52
 53        // Set up event handlers
 054        _peerCommunicationService.MessageReceived += HandleMessage;
 055        _peerCommunicationService.ExceptionRaised += HandleException;
 056        _peerCommunicationService.DisconnectEvent += HandleDisconnection;
 57
 58        // Initialize communication
 059        _peerCommunicationService.InitializeAsync(networkTimeout).GetAwaiter().GetResult();
 060    }
 61
 62    /// <summary>
 63    /// Disconnects from the peer.
 64    /// </summary>
 65    public void Disconnect()
 66    {
 067        _logger.LogInformation("Disconnecting peer {peer}", PeerPubKey);
 068        _peerCommunicationService.Disconnect();
 069    }
 70
 71    public Task SendMessageAsync(IChannelMessage replyMessage)
 72    {
 073        return _peerCommunicationService.SendMessageAsync(replyMessage);
 74    }
 75
 76    /// <summary>
 77    /// Handles messages received from the peer.
 78    /// </summary>
 79    private void HandleMessage(object? sender, IMessage? message)
 80    {
 081        if (message is null)
 082            return;
 83
 084        if (!_isInitialized)
 85        {
 086            _logger.LogTrace("Received message from peer {peer} but was not initialized", PeerPubKey);
 087            HandleInitialization(message);
 88        }
 089        else if (message is IChannelMessage channelMessage)
 90        {
 91            // Handle channel-related messages
 092            HandleChannelMessage(channelMessage);
 93        }
 094    }
 95
 96    /// <summary>
 97    /// Handles exceptions raised by the communication service.
 98    /// </summary>
 99    private void HandleException(object? sender, Exception e)
 100    {
 0101        _logger.LogError(e, "Exception occurred with peer {peer}", PeerPubKey);
 0102    }
 103
 104    private void HandleDisconnection(object? sender, EventArgs e)
 105    {
 0106        OnDisconnect?.Invoke(this, new PeerDisconnectedEventArgs(PeerPubKey));
 0107    }
 108
 109    /// <summary>
 110    /// Handles the initialization process when receiving the first message.
 111    /// </summary>
 112    private void HandleInitialization(IMessage message)
 113    {
 114        // Check if the first message is an init message
 0115        if (message.Type != MessageTypes.Init || message is not InitMessage initMessage)
 116        {
 0117            _logger.LogError("Failed to receive init message from peer {peer}", PeerPubKey);
 0118            Disconnect();
 0119            return;
 120        }
 121
 122        // Check if Features are compatible
 0123        if (!Features.GetNodeFeatures().IsCompatible(initMessage.Payload.FeatureSet, out var negotiatedFeatures)
 0124         || negotiatedFeatures is null)
 125        {
 0126            _logger.LogError("Peer {peer} is not compatible", PeerPubKey);
 0127            Disconnect();
 0128            return;
 129        }
 130
 131        // Check if ChainHash contained in networksTlv.ChainHashes exists in our ChainHashes
 0132        var networkChainHashes = initMessage.NetworksTlv?.ChainHashes;
 0133        if (networkChainHashes != null
 0134         && networkChainHashes.Any(chainHash => !Features.ChainHashes.Contains(chainHash)))
 135        {
 0136            _logger.LogError("Peer {peer} chain is not compatible", PeerPubKey);
 0137            Disconnect();
 0138            return;
 139        }
 140
 0141        if (initMessage.RemoteAddressTlv is not null)
 142        {
 0143            switch (initMessage.RemoteAddressTlv.AddressType)
 144            {
 145                case 1 or 2:
 146                    {
 0147                        if (!IPAddress.TryParse(initMessage.RemoteAddressTlv.Address, out var ipAddress))
 148                        {
 0149                            _logger.LogWarning("Peer {peer} has an invalid remote address: {address}",
 0150                                               PeerPubKey, initMessage.RemoteAddressTlv.Address);
 151                        }
 152                        else
 153                        {
 0154                            PreferredHost = ipAddress.ToString();
 0155                            PreferredPort = initMessage.RemoteAddressTlv.Port;
 156                        }
 157
 0158                        break;
 159                    }
 160                case 5:
 0161                    PreferredHost = initMessage.RemoteAddressTlv.Address;
 0162                    PreferredPort = initMessage.RemoteAddressTlv.Port;
 0163                    break;
 164                default:
 0165                    _logger.LogWarning("Peer {peer} has an unsupported remote address type: {addressType}",
 0166                                       PeerPubKey, initMessage.RemoteAddressTlv.AddressType);
 167                    break;
 168            }
 169        }
 170
 0171        Features = FeatureOptions.GetNodeOptions(negotiatedFeatures, initMessage.Extension);
 0172        _logger.LogTrace("Initialization from peer {peer} completed successfully", PeerPubKey);
 0173        _isInitialized = true;
 0174    }
 175
 176    /// <summary>
 177    /// Handles channel messages.
 178    /// </summary>
 179    private void HandleChannelMessage(IChannelMessage message)
 180    {
 0181        _logger.LogTrace("Received channel message ({messageType}) from peer {peer}",
 0182                         Enum.GetName(message.Type), PeerPubKey);
 183
 0184        OnChannelMessageReceived?.Invoke(this, new ChannelMessageEventArgs(message, PeerPubKey));
 0185    }
 186}