< Summary - Combined Code Coverage

Information
Class: NLightning.Application.Channels.Handlers.FundingSignedMessageHandler
Assembly: NLightning.Application
File(s): /home/runner/work/NLightning/NLightning/src/NLightning.Application/Channels/Handlers/FundingSignedMessageHandler.cs
Tag: 57_24045730253
Line coverage
0%
Covered lines: 0
Uncovered lines: 59
Coverable lines: 59
Total lines: 141
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 12
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
HandleAsync()0%7280%
PersistChannelAsync()0%2040%

File(s)

/home/runner/work/NLightning/NLightning/src/NLightning.Application/Channels/Handlers/FundingSignedMessageHandler.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2
 3namespace NLightning.Application.Channels.Handlers;
 4
 5using Domain.Bitcoin.Interfaces;
 6using Domain.Bitcoin.Transactions.Enums;
 7using Domain.Bitcoin.Transactions.Interfaces;
 8using Domain.Channels.Enums;
 9using Domain.Channels.Interfaces;
 10using Domain.Channels.Models;
 11using Domain.Crypto.ValueObjects;
 12using Domain.Exceptions;
 13using Domain.Node.Options;
 14using Domain.Persistence.Interfaces;
 15using Domain.Protocol.Interfaces;
 16using Domain.Protocol.Messages;
 17using Infrastructure.Bitcoin.Builders.Interfaces;
 18using Infrastructure.Bitcoin.Wallet.Interfaces;
 19using Interfaces;
 20
 21public class FundingSignedMessageHandler : IChannelMessageHandler<FundingSignedMessage>
 22{
 23    private readonly IBlockchainMonitor _blockchainMonitor;
 24    private readonly IChannelMemoryRepository _channelMemoryRepository;
 25    private readonly ICommitmentTransactionBuilder _commitmentTransactionBuilder;
 26    private readonly ICommitmentTransactionModelFactory _commitmentTransactionModelFactory;
 27    private readonly IFundingTransactionBuilder _fundingTransactionBuilder;
 28    private readonly IFundingTransactionModelFactory _fundingTransactionModelFactory;
 29    private readonly ILightningSigner _lightningSigner;
 30    private readonly ILogger<FundingSignedMessageHandler> _logger;
 31    private readonly IUnitOfWork _unitOfWork;
 32    private readonly IUtxoMemoryRepository _utxoMemoryRepository;
 33
 034    public FundingSignedMessageHandler(IBlockchainMonitor blockchainMonitor,
 035                                       IChannelMemoryRepository channelMemoryRepository,
 036                                       ICommitmentTransactionBuilder commitmentTransactionBuilder,
 037                                       ICommitmentTransactionModelFactory commitmentTransactionModelFactory,
 038                                       IFundingTransactionBuilder fundingTransactionBuilder,
 039                                       IFundingTransactionModelFactory fundingTransactionModelFactory,
 040                                       ILightningSigner lightningSigner, ILogger<FundingSignedMessageHandler> logger,
 041                                       IUnitOfWork unitOfWork, IUtxoMemoryRepository utxoMemoryRepository)
 42    {
 043        _blockchainMonitor = blockchainMonitor;
 044        _channelMemoryRepository = channelMemoryRepository;
 045        _commitmentTransactionBuilder = commitmentTransactionBuilder;
 046        _commitmentTransactionModelFactory = commitmentTransactionModelFactory;
 047        _fundingTransactionBuilder = fundingTransactionBuilder;
 048        _fundingTransactionModelFactory = fundingTransactionModelFactory;
 049        _lightningSigner = lightningSigner;
 050        _logger = logger;
 051        _unitOfWork = unitOfWork;
 052        _utxoMemoryRepository = utxoMemoryRepository;
 053    }
 54
 55    public async Task<IChannelMessage?> HandleAsync(FundingSignedMessage message, ChannelState currentState,
 56                                                    FeatureOptions negotiatedFeatures, CompactPubKey peerPubKey)
 57    {
 058        if (_logger.IsEnabled(LogLevel.Trace))
 059            _logger.LogTrace("Processing FundingCreatedMessage with ChannelId: {ChannelId} from Peer: {PeerPubKey}",
 060                             message.Payload.ChannelId, peerPubKey);
 61
 062        var payload = message.Payload;
 63
 064        if (currentState != ChannelState.V1FundingCreated)
 065            throw new ChannelErrorException(
 066                $"Received funding signed, but the channel {payload.ChannelId} had the wrong state: {Enum.GetName(curren
 67
 68        // Check if there's a temporary channel for this peer
 069        if (!_channelMemoryRepository.TryGetChannel(payload.ChannelId, out var channel))
 070            throw new ChannelErrorException("This channel has never been negotiated", payload.ChannelId);
 71
 72        // Generate the base commitment transactions
 073        var localCommitmentTransaction =
 074            _commitmentTransactionModelFactory.CreateCommitmentTransactionModel(channel, CommitmentSide.Local);
 75
 76        // Build the output and the transactions
 077        var localUnsignedCommitmentTransaction = _commitmentTransactionBuilder.Build(localCommitmentTransaction);
 78
 79        // Validate remote signature for our local commitment transaction
 080        _lightningSigner.ValidateSignature(channel.ChannelId, payload.Signature, localUnsignedCommitmentTransaction);
 81
 82        // Update the channel with the new signature
 083        channel.UpdateLastReceivedSignature(payload.Signature);
 84
 85        // Get the locked utxos to create the funding transaction
 086        var utxos = _utxoMemoryRepository.GetLockedUtxosForChannel(channel.ChannelId);
 87
 88        // Get a change address in case we need one
 089        var fundingTransactionModel = _fundingTransactionModelFactory.Create(channel, utxos, channel.ChangeAddress);
 090        var unsignedFundingTransaction = _fundingTransactionBuilder.Build(fundingTransactionModel);
 91
 92        // Sign the transaction
 093        var allSigned = _lightningSigner.SignFundingTransaction(channel.ChannelId, unsignedFundingTransaction);
 094        if (!allSigned)
 095            throw new ChannelErrorException("Unable to sign all inputs for the funding transaction");
 96
 97        // Persist the channel to the database before publishing the transaction, so the watched transaction can point
 98        // to the channel
 099        await PersistChannelAsync(channel);
 100
 0101        await _blockchainMonitor.PublishAndWatchTransactionAsync(channel.ChannelId, unsignedFundingTransaction,
 0102                                                                 channel.ChannelConfig.MinimumDepth);
 103
 104        // Now that we should remember the channel, we update its state
 0105        channel.UpdateState(ChannelState.V1FundingSigned);
 106
 107        // Save to the database
 0108        await PersistChannelAsync(channel);
 109
 0110        return null;
 0111    }
 112
 113    /// <summary>
 114    /// Persists a channel to the database using a scoped Unit of Work
 115    /// </summary>
 116    private async Task PersistChannelAsync(ChannelModel channel)
 117    {
 118        try
 119        {
 120            // Update the channel in memory first
 0121            _channelMemoryRepository.UpdateChannel(channel);
 122
 123            // Check if we are adding or if we need to update the channel
 0124            var existingChannel = await _unitOfWork.ChannelDbRepository.GetByIdAsync(channel.ChannelId);
 0125            if (existingChannel is not null)
 0126                await _unitOfWork.ChannelDbRepository.UpdateAsync(channel);
 127            else
 0128                await _unitOfWork.ChannelDbRepository.AddAsync(channel);
 129
 0130            await _unitOfWork.SaveChangesAsync();
 131
 0132            if (_logger.IsEnabled(LogLevel.Debug))
 0133                _logger.LogDebug("Successfully persisted channel {ChannelId} to database", channel.ChannelId);
 0134        }
 0135        catch (Exception ex)
 136        {
 0137            _logger.LogError(ex, "Failed to persist channel {ChannelId} to database", channel.ChannelId);
 0138            throw;
 139        }
 0140    }
 141}