< Summary - Combined Code Coverage

Information
Class: NLightning.Application.Channels.Handlers.AcceptChannel1MessageHandler
Assembly: NLightning.Application
File(s): /home/runner/work/NLightning/NLightning/src/NLightning.Application/Channels/Handlers/AcceptChannel1MessageHandler.cs
Tag: 57_24045730253
Line coverage
0%
Covered lines: 0
Uncovered lines: 122
Coverable lines: 122
Total lines: 244
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 40
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%1640400%

File(s)

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

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using NLightning.Domain.Persistence.Interfaces;
 3
 4namespace NLightning.Application.Channels.Handlers;
 5
 6using Domain.Bitcoin.Enums;
 7using Domain.Bitcoin.Interfaces;
 8using Domain.Bitcoin.Transactions.Enums;
 9using Domain.Bitcoin.Transactions.Interfaces;
 10using Domain.Bitcoin.Transactions.Outputs;
 11using Domain.Bitcoin.ValueObjects;
 12using Domain.Channels.Enums;
 13using Domain.Channels.Interfaces;
 14using Domain.Channels.Models;
 15using Domain.Channels.Validators.Parameters;
 16using Domain.Channels.ValueObjects;
 17using Domain.Crypto.Hashes;
 18using Domain.Crypto.ValueObjects;
 19using Domain.Enums;
 20using Domain.Exceptions;
 21using Domain.Node.Options;
 22using Domain.Protocol.Interfaces;
 23using Domain.Protocol.Messages;
 24using Domain.Protocol.Models;
 25using Infrastructure.Bitcoin.Builders.Interfaces;
 26using Infrastructure.Bitcoin.Wallet.Interfaces;
 27using Interfaces;
 28
 29public class AcceptChannel1MessageHandler : IChannelMessageHandler<AcceptChannel1Message>
 30{
 31    private readonly IBitcoinWalletService _bitcoinWalletService;
 32    private readonly IChannelIdFactory _channelIdFactory;
 33    private readonly IChannelMemoryRepository _channelMemoryRepository;
 34    private readonly IChannelOpenValidator _channelOpenValidator;
 35    private readonly ICommitmentTransactionBuilder _commitmentTransactionBuilder;
 36    private readonly ICommitmentTransactionModelFactory _commitmentTransactionModelFactory;
 37    private readonly IFundingTransactionBuilder _fundingTransactionBuilder;
 38    private readonly IFundingTransactionModelFactory _fundingTransactionModelFactory;
 39    private readonly ILightningSigner _lightningSigner;
 40    private readonly ILogger<OpenChannel1MessageHandler> _logger;
 41    private readonly IMessageFactory _messageFactory;
 42    private readonly ISha256 _sha256;
 43    private readonly IUnitOfWork _unitOfWork;
 44    private readonly IUtxoMemoryRepository _utxoMemoryRepository;
 45
 046    public AcceptChannel1MessageHandler(IBitcoinWalletService bitcoinWalletService, IChannelIdFactory channelIdFactory,
 047                                        IChannelMemoryRepository channelMemoryRepository,
 048                                        IChannelOpenValidator channelOpenValidator,
 049                                        ICommitmentTransactionBuilder commitmentTransactionBuilder,
 050                                        ICommitmentTransactionModelFactory commitmentTransactionModelFactory,
 051                                        IFundingTransactionBuilder fundingTransactionBuilder,
 052                                        IFundingTransactionModelFactory fundingTransactionModelFactory,
 053                                        ILightningSigner lightningSigner, ILogger<OpenChannel1MessageHandler> logger,
 054                                        IMessageFactory messageFactory, ISha256 sha256, IUnitOfWork unitOfWork,
 055                                        IUtxoMemoryRepository utxoMemoryRepository)
 56    {
 057        _bitcoinWalletService = bitcoinWalletService;
 058        _channelIdFactory = channelIdFactory;
 059        _channelMemoryRepository = channelMemoryRepository;
 060        _channelOpenValidator = channelOpenValidator;
 061        _commitmentTransactionBuilder = commitmentTransactionBuilder;
 062        _commitmentTransactionModelFactory = commitmentTransactionModelFactory;
 063        _fundingTransactionBuilder = fundingTransactionBuilder;
 064        _fundingTransactionModelFactory = fundingTransactionModelFactory;
 065        _lightningSigner = lightningSigner;
 066        _logger = logger;
 067        _messageFactory = messageFactory;
 068        _sha256 = sha256;
 069        _unitOfWork = unitOfWork;
 070        _utxoMemoryRepository = utxoMemoryRepository;
 071    }
 72
 73    public async Task<IChannelMessage?> HandleAsync(AcceptChannel1Message message, ChannelState currentState,
 74                                                    FeatureOptions negotiatedFeatures, CompactPubKey peerPubKey)
 75    {
 076        if (_logger.IsEnabled(LogLevel.Trace))
 077            _logger.LogTrace("Processing AcceptChannel1Message with ChannelId: {ChannelId} from Peer: {PeerPubKey}",
 078                             message.Payload.ChannelId, peerPubKey);
 79
 080        var payload = message.Payload;
 81
 082        if (currentState != ChannelState.None)
 083            throw new ChannelErrorException("A channel with this id already exists", payload.ChannelId);
 84
 85        // Check if there's a temporary channel for this peer
 086        if (_channelMemoryRepository.TryGetTemporaryChannelState(peerPubKey, payload.ChannelId, out currentState))
 87        {
 088            if (currentState != ChannelState.V1Opening)
 89            {
 090                throw new ChannelErrorException("Channel had the wrong state", payload.ChannelId,
 091                                                "This channel is already being negotiated with peer");
 92            }
 93        }
 94
 95        // Get the temporary channel
 096        if (!_channelMemoryRepository.TryGetTemporaryChannel(peerPubKey, payload.ChannelId, out var tempChannel))
 097            throw new ChannelErrorException("Temporary channel not found", payload.ChannelId);
 98
 99        // Check if the channel type was negotiated and the channel type is present
 0100        if (message.ChannelTypeTlv is null)
 0101            throw new ChannelErrorException("Channel type was not provided");
 102
 103        // Perform optional checks for the channel
 0104        _channelOpenValidator.PerformOptionalChecks(
 0105            ChannelOpenOptionalValidationParameters.FromAcceptChannel1Payload(
 0106                payload, tempChannel.ChannelConfig.ChannelReserveAmount));
 107
 108        // Perform mandatory checks for the channel
 0109        _channelOpenValidator.PerformMandatoryChecks(ChannelOpenMandatoryValidationParameters.FromAcceptChannel1Payload(
 0110                                                         message.ChannelTypeTlv,
 0111                                                         tempChannel.ChannelConfig.FeeRateAmountPerKw,
 0112                                                         negotiatedFeatures, payload), out var minimumDepth);
 113
 0114        if (minimumDepth != tempChannel.ChannelConfig.MinimumDepth)
 0115            throw new ChannelErrorException("Minimum depth is not acceptable", payload.ChannelId);
 116
 117        // Check for the upfront shutdown script
 0118        if (message.UpfrontShutdownScriptTlv is null
 0119         && (negotiatedFeatures.UpfrontShutdownScript > FeatureSupport.No || message.ChannelTypeTlv is not null))
 0120            throw new ChannelErrorException("Upfront shutdown script is required but not provided");
 121
 0122        BitcoinScript? remoteUpfrontShutdownScript = null;
 0123        if (message.UpfrontShutdownScriptTlv is not null && message.UpfrontShutdownScriptTlv.Value.Length > 0)
 0124            remoteUpfrontShutdownScript = message.UpfrontShutdownScriptTlv.Value;
 125
 126        // Create the remote key set from the message
 0127        var remoteKeySet = ChannelKeySetModel.CreateForRemote(message.Payload.FundingPubKey,
 0128                                                              message.Payload.RevocationBasepoint,
 0129                                                              message.Payload.PaymentBasepoint,
 0130                                                              message.Payload.DelayedPaymentBasepoint,
 0131                                                              message.Payload.HtlcBasepoint,
 0132                                                              message.Payload.FirstPerCommitmentPoint);
 133
 0134        tempChannel.AddRemoteKeySet(remoteKeySet);
 135
 136        // Create a new ChannelConfig with the remote-provided values
 0137        var channelConfig = new ChannelConfig(tempChannel.ChannelConfig.ChannelReserveAmount,
 0138                                              tempChannel.ChannelConfig.FeeRateAmountPerKw,
 0139                                              tempChannel.ChannelConfig.HtlcMinimumAmount,
 0140                                              tempChannel.ChannelConfig.LocalDustLimitAmount,
 0141                                              tempChannel.ChannelConfig.MaxAcceptedHtlcs,
 0142                                              tempChannel.ChannelConfig.MaxHtlcAmountInFlight,
 0143                                              tempChannel.ChannelConfig.MinimumDepth,
 0144                                              tempChannel.ChannelConfig.OptionAnchorOutputs,
 0145                                              payload.DustLimitAmount, payload.ToSelfDelay,
 0146                                              tempChannel.ChannelConfig.UseScidAlias,
 0147                                              tempChannel.ChannelConfig.LocalUpfrontShutdownScript,
 0148                                              remoteUpfrontShutdownScript);
 149
 0150        tempChannel.UpdateChannelConfig(channelConfig);
 151
 152        // Generate the correct commitment number
 0153        var commitmentNumber = new CommitmentNumber(tempChannel.LocalKeySet.PaymentCompactBasepoint,
 0154                                                    remoteKeySet.PaymentCompactBasepoint, _sha256);
 155
 0156        tempChannel.AddCommitmentNumber(commitmentNumber);
 157
 158        // Keep the oldChannelId for later
 0159        var oldChannelId = tempChannel.ChannelId;
 160
 161        try
 162        {
 0163            var fundingAmount = tempChannel.LocalBalance + tempChannel.RemoteBalance;
 0164            var fundingOutput = new FundingOutputInfo(fundingAmount, tempChannel.LocalKeySet.FundingCompactPubKey,
 0165                                                      remoteKeySet.FundingCompactPubKey);
 166
 0167            tempChannel.AddFundingOutput(fundingOutput);
 168
 169            // Get the utxos to create the funding transaction
 0170            var utxos = _utxoMemoryRepository.GetLockedUtxosForChannel(tempChannel.ChannelId);
 171
 172            // Get a change address in case we need one
 0173            var walletAddress = await _bitcoinWalletService.GetUnusedAddressAsync(AddressType.P2Wpkh, true);
 174
 175            // Create the funding transaction
 0176            var fundingTransactionModel = _fundingTransactionModelFactory.Create(tempChannel, utxos, walletAddress);
 0177            _ = _fundingTransactionBuilder.Build(fundingTransactionModel);
 0178            if (fundingOutput.TransactionId is null || fundingOutput.Index is null)
 0179                throw new ChannelErrorException("Error building the funding transaction");
 180
 181            // If a change was needed, save the change data to the channel
 0182            if (fundingTransactionModel.ChangeAddress is not null)
 0183                tempChannel.ChangeAddress = fundingTransactionModel.ChangeAddress;
 184
 185            // Create a new channelId
 0186            tempChannel.UpdateChannelId(
 0187                _channelIdFactory.CreateV1(fundingOutput.TransactionId.Value, fundingOutput.Index.Value));
 188
 189            // Check if the channel already exists in the database (it never should)
 0190            var existingChannel = await _unitOfWork.ChannelDbRepository.GetByIdAsync(tempChannel.ChannelId);
 0191            if (existingChannel is not null)
 0192                throw new ChannelErrorException("Channel already exists in the database", tempChannel.ChannelId,
 0193                                                "Sorry, we had an internal error");
 194
 195            // Register the channel with the signer
 0196            _lightningSigner.RegisterChannel(tempChannel.ChannelId, tempChannel.GetSigningInfo());
 197
 198            // Generate the base commitment transactions
 0199            var remoteCommitmentTransaction =
 0200                _commitmentTransactionModelFactory.CreateCommitmentTransactionModel(tempChannel, CommitmentSide.Remote);
 201
 202            // Build the output and the transactions
 0203            var remoteUnsignedCommitmentTransaction = _commitmentTransactionBuilder.Build(remoteCommitmentTransaction);
 204
 205            // Sign their remote commitment transaction
 0206            var ourSignature =
 0207                _lightningSigner.SignChannelTransaction(tempChannel.ChannelId, remoteUnsignedCommitmentTransaction);
 208
 209            // Update the channel with the new signature and the new state
 0210            tempChannel.UpdateLastSentSignature(ourSignature);
 0211            tempChannel.UpdateState(ChannelState.V1FundingCreated);
 212
 213            // Create the funding created message
 0214            var fundingCreatedMessage =
 0215                _messageFactory.CreateFundingCreatedMessage(oldChannelId, fundingOutput.TransactionId.Value,
 0216                                                            fundingOutput.Index.Value, ourSignature);
 217
 218            // Upgrade the channel in the dictionary
 0219            _channelMemoryRepository.UpgradeChannel(oldChannelId, tempChannel);
 220
 221            // Update the locked utxos
 0222            _utxoMemoryRepository.UpgradeChannelIdOnLockedUtxos(oldChannelId, tempChannel.ChannelId);
 223
 0224            return fundingCreatedMessage;
 225        }
 0226        catch (Exception e)
 227        {
 0228            if (_logger.IsEnabled(LogLevel.Information))
 0229                _logger.LogInformation("Forgetting channel {channelId}", tempChannel.ChannelId);
 230
 0231            if (tempChannel.ChannelId != oldChannelId)
 232            {
 0233                if (!_channelMemoryRepository.TryRemoveTemporaryChannel(tempChannel.RemoteNodeId,
 0234                                                                        tempChannel.ChannelId))
 0235                    _logger.LogWarning("Unable to remove temporary channel with id {channelId} for peer {peerPubKey}",
 0236                                       tempChannel.ChannelId, peerPubKey);
 0237                else if (!_channelMemoryRepository.TryRemoveChannel(tempChannel.ChannelId))
 0238                    _logger.LogWarning("Unable to remove channel with id {channelId}", tempChannel.ChannelId);
 239            }
 240
 0241            throw new ChannelErrorException("Error creating commitment transaction", e);
 242        }
 0243    }
 244}