< Summary - Combined Code Coverage

Information
Class: NLightning.Application.Channels.Handlers.FundingCreatedMessageHandler
Assembly: NLightning.Application
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Application/Channels/Handlers/FundingCreatedMessageHandler.cs
Tag: 36_15743069263
Line coverage
100%
Covered lines: 63
Uncovered lines: 0
Coverable lines: 63
Total lines: 144
Line coverage: 100%
Branch coverage
100%
Covered branches: 10
Total branches: 10
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
HandleAsync()100%88100%
PersistChannelAsync()100%22100%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Application/Channels/Handlers/FundingCreatedMessageHandler.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using NLightning.Domain.Bitcoin.Transactions.Enums;
 3using NLightning.Domain.Bitcoin.Transactions.Interfaces;
 4using NLightning.Infrastructure.Bitcoin.Builders.Interfaces;
 5using NLightning.Infrastructure.Bitcoin.Wallet.Interfaces;
 6
 7namespace NLightning.Application.Channels.Handlers;
 8
 9using Domain.Bitcoin.Interfaces;
 10using Domain.Channels.Enums;
 11using Domain.Channels.Interfaces;
 12using Domain.Channels.Models;
 13using Domain.Crypto.ValueObjects;
 14using Domain.Exceptions;
 15using Domain.Node.Options;
 16using Domain.Persistence.Interfaces;
 17using Domain.Protocol.Interfaces;
 18using Domain.Protocol.Messages;
 19using Interfaces;
 20
 21public class FundingCreatedMessageHandler : IChannelMessageHandler<FundingCreatedMessage>
 22{
 23    private readonly IBlockchainMonitor _blockchainMonitor;
 24    private readonly IChannelIdFactory _channelIdFactory;
 25    private readonly IChannelMemoryRepository _channelMemoryRepository;
 26    private readonly ICommitmentTransactionBuilder _commitmentTransactionBuilder;
 27    private readonly ICommitmentTransactionModelFactory _commitmentTransactionModelFactory;
 28    private readonly ILightningSigner _lightningSigner;
 29    private readonly ILogger<FundingCreatedMessageHandler> _logger;
 30    private readonly IMessageFactory _messageFactory;
 31    private readonly IUnitOfWork _unitOfWork;
 32
 2433    public FundingCreatedMessageHandler(IBlockchainMonitor blockchainMonitor, IChannelIdFactory channelIdFactory,
 2434                                        IChannelMemoryRepository channelMemoryRepository,
 2435                                        ICommitmentTransactionBuilder commitmentTransactionBuilder,
 2436                                        ICommitmentTransactionModelFactory commitmentTransactionModelFactory,
 2437                                        ILightningSigner lightningSigner, ILogger<FundingCreatedMessageHandler> logger,
 2438                                        IMessageFactory messageFactory, IUnitOfWork unitOfWork)
 39    {
 2440        _blockchainMonitor = blockchainMonitor;
 2441        _channelIdFactory = channelIdFactory;
 2442        _channelMemoryRepository = channelMemoryRepository;
 2443        _commitmentTransactionBuilder = commitmentTransactionBuilder;
 2444        _commitmentTransactionModelFactory = commitmentTransactionModelFactory;
 2445        _lightningSigner = lightningSigner;
 2446        _logger = logger;
 2447        _messageFactory = messageFactory;
 2448        _unitOfWork = unitOfWork;
 2449    }
 50
 51    public async Task<IChannelMessage?> HandleAsync(FundingCreatedMessage message, ChannelState currentState,
 52                                                    FeatureOptions negotiatedFeatures, CompactPubKey peerPubKey)
 53    {
 2454        _logger.LogTrace("Processing FundingCreatedMessage with ChannelId: {ChannelId} from Peer: {PeerPubKey}",
 2455                         message.Payload.ChannelId, peerPubKey);
 56
 2457        var payload = message.Payload;
 58
 2459        if (currentState != ChannelState.None)
 460            throw new ChannelErrorException("A channel with this id already exists", payload.ChannelId);
 61
 62        // Check if there's a temporary channel for this peer
 2063        if (!_channelMemoryRepository.TryGetTemporaryChannelState(peerPubKey, payload.ChannelId, out currentState))
 464            throw new ChannelErrorException("This channel has never been negotiated", payload.ChannelId);
 65
 1666        if (currentState != ChannelState.V1Opening)
 467            throw new ChannelErrorException("Channel had the wrong state", payload.ChannelId,
 468                                            "This channel is already being negotiated with peer");
 69
 70        // Get the channel and set missing props
 1271        if (!_channelMemoryRepository.TryGetTemporaryChannel(peerPubKey, payload.ChannelId, out var channel))
 472            throw new ChannelErrorException("Temporary channel not found", payload.ChannelId);
 73
 874        channel.FundingOutput.TransactionId = payload.FundingTxId;
 875        channel.FundingOutput.Index = payload.FundingOutputIndex;
 76
 77        // Create a new channelId
 878        var oldChannelId = channel.ChannelId;
 879        channel.UpdateChannelId(_channelIdFactory.CreateV1(payload.FundingTxId, payload.FundingOutputIndex));
 80
 81        // Register the channel with the signer
 882        _lightningSigner.RegisterChannel(channel.ChannelId, channel.GetSigningInfo());
 83
 84        // Generate the base commitment transactions
 885        var localCommitmentTransaction =
 886            _commitmentTransactionModelFactory.CreateCommitmentTransactionModel(channel, CommitmentSide.Local);
 887        var remoteCommitmentTransaction =
 888            _commitmentTransactionModelFactory.CreateCommitmentTransactionModel(channel, CommitmentSide.Remote);
 89
 90        // Build the output and the transactions
 891        var localUnsignedCommitmentTransaction = _commitmentTransactionBuilder.Build(localCommitmentTransaction);
 892        var remoteUnsignedCommitmentTransaction = _commitmentTransactionBuilder.Build(remoteCommitmentTransaction);
 93
 94        // Validate remote signature for our local commitment transaction
 895        _lightningSigner.ValidateSignature(channel.ChannelId, payload.Signature, localUnsignedCommitmentTransaction);
 96
 97        // Sign our remote commitment transaction
 898        var ourSignature = _lightningSigner.SignTransaction(channel.ChannelId, remoteUnsignedCommitmentTransaction);
 99
 8100        channel.UpdateState(ChannelState.V1FundingSigned);
 101        // Save to the database
 8102        await PersistChannelAsync(channel);
 103
 104        // Create the funding signed message
 4105        var fundingSignedMessage =
 4106            _messageFactory.CreatedFundingSignedMessage(channel.ChannelId, ourSignature);
 107
 108        // Add the channel to the dictionary
 4109        _channelMemoryRepository.AddChannel(channel);
 110
 111        // Remove the temporary channel
 4112        _channelMemoryRepository.RemoveTemporaryChannel(peerPubKey, oldChannelId);
 113
 4114        await _blockchainMonitor.WatchTransactionAsync(channel.ChannelId, payload.FundingTxId,
 4115                                                       channel.ChannelConfig.MinimumDepth);
 116
 4117        return fundingSignedMessage;
 4118    }
 119
 120    /// <summary>
 121    /// Persists a channel to the database using the scoped Unit of Work
 122    /// </summary>
 123    private async Task PersistChannelAsync(ChannelModel channel)
 124    {
 125        try
 126        {
 127            // Check if the channel already exists
 8128            var existingChannel = await _unitOfWork.ChannelDbRepository.GetByIdAsync(channel.ChannelId);
 8129            if (existingChannel is not null)
 4130                throw new ChannelWarningException("Channel already exists", channel.ChannelId,
 4131                                                  "This channel is already in our database");
 132
 4133            await _unitOfWork.ChannelDbRepository.AddAsync(channel);
 4134            await _unitOfWork.SaveChangesAsync();
 135
 4136            _logger.LogDebug("Successfully persisted channel {ChannelId} to database", channel.ChannelId);
 4137        }
 4138        catch (Exception ex)
 139        {
 4140            _logger.LogError(ex, "Failed to persist channel {ChannelId} to database", channel.ChannelId);
 4141            throw;
 142        }
 4143    }
 144}