Skip to content

Commit

Permalink
Merge pull request #270 from Elenpay/mark-closed-channels-as-closed
Browse files Browse the repository at this point in the history
Mark closed channel as closed
  • Loading branch information
RodriFS authored Aug 17, 2023
2 parents 5857bc7 + 2bb0930 commit 695b2b7
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NodeGuard.Helpers;

public class ChannelStatus
public class ChannelState
{
public long LocalBalance { get; set; }
public long RemoteBalance { get; set; }
Expand Down
28 changes: 27 additions & 1 deletion src/Jobs/ChannelMonitorJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using Microsoft.EntityFrameworkCore;
using Quartz;
using Channel = Lnrpc.Channel;
using ChannelStatus = NodeGuard.Data.Models.Channel.ChannelStatus;

namespace NodeGuard.Jobs;

Expand Down Expand Up @@ -68,7 +69,9 @@ public async Task Execute(IJobExecutionContext context)

var result = await _lightningClientService.ListChannels(node1);

foreach (var channel in result?.Channels)
var channels = result?.Channels.ToList();
await MarkClosedChannelsAsClosed(node1, channels);
foreach (var channel in channels)
{
var node2 = await _nodeRepository.GetOrCreateByPubKey(channel.RemotePubkey, _lightningService);

Expand Down Expand Up @@ -144,4 +147,27 @@ public async Task RecoverChannelInConfirmationPendingStatus(Node source)
_logger.LogError(e, "Error while recovering channel in OnChainConfirmationPending status, {SourceNodeId}: {Error}", source.Id, e);
}
}

public async Task MarkClosedChannelsAsClosed(Node source, List<Channel>? channels)
{
if (channels == null) return;
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
try
{
var openChannels = dbContext.Channels.Where(c => (c.SourceNodeId == source.Id || c.DestinationNodeId == source.Id) && c.Status == ChannelStatus.Open).ToList();
foreach (var openChannel in openChannels)
{
var channel = channels.FirstOrDefault(c => c.ChanId == openChannel.ChanId);
if (channel == null)
{
openChannel.Status = ChannelStatus.Closed;
await dbContext.SaveChangesAsync();
}
}
}
catch (Exception e)
{
_logger.LogError(e, "Error while marking closed channels as closed, {SourceNodeId}: {Error}", source.Id, e);
}
}
}
7 changes: 4 additions & 3 deletions src/Pages/Channels.razor
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@
private Dictionary<string, bool> _channelsColumns = new();
private bool _columnsLoaded;
// This dictionary is used to store the balance of each channel, key is the channel id, value is a tuple with the node id as local in the pair, local balance, remote balance
private Dictionary<ulong, ChannelStatus> _channelsBalance = new();
private Dictionary<ulong, ChannelState> _channelsBalance = new();

public abstract class ChannelsColumnName
{
Expand Down Expand Up @@ -418,7 +418,7 @@
_nodes = await NodeRepository.GetAll();
_wallets = await WalletRepository.GetAll();
_channelsDataGridRef?.FilterData();
_channelsBalance = await LightningService.GetChannelsStatus();
_channelsBalance = await LightningService.GetChannelsState();
}

protected override async Task OnAfterRenderAsync(bool firstRender)
Expand Down Expand Up @@ -856,7 +856,8 @@

private async Task RefreshChannelInformation()
{
_channelsBalance = await LightningService.GetChannelsStatus();
await FetchData();
_channelsBalance = await LightningService.GetChannelsState();
StateHasChanged();
}

Expand Down
8 changes: 4 additions & 4 deletions src/Services/LightningService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public interface ILightningService
/// Gets a dictionary of the local and remote balance of all the channels managed by NG
/// </summary>
/// <returns></returns>
public Task<Dictionary<ulong, ChannelStatus>> GetChannelsStatus();
public Task<Dictionary<ulong, ChannelState>> GetChannelsState();

/// <summary>
/// Cancels a pending channel from LND PSBT-based funding of channels
Expand Down Expand Up @@ -1289,11 +1289,11 @@ public async Task CloseChannel(ChannelOperationRequest channelOperationRequest,
return await _lightningClientService.GetNodeInfo(node, pubkey);
}

public async Task<Dictionary<ulong, ChannelStatus>> GetChannelsStatus()
public async Task<Dictionary<ulong, ChannelState>> GetChannelsState()
{
var nodes = await _nodeRepository.GetAllManagedByNodeGuard();

var result = new Dictionary<ulong, ChannelStatus>();
var result = new Dictionary<ulong, ChannelState>();
foreach (var node in nodes)
{
var listChannelsResponse = await _lightningClientService.ListChannels(node);
Expand All @@ -1312,7 +1312,7 @@ public async Task<Dictionary<ulong, ChannelStatus>> GetChannelsStatus()
var localBalance = channel.LocalBalance + htlcsLocal;
var remoteBalance = channel.RemoteBalance + htlcsRemote;

result.TryAdd(channel.ChanId, new ChannelStatus()
result.TryAdd(channel.ChanId, new ChannelState()
{
LocalBalance = localBalance,
RemoteBalance = remoteBalance,
Expand Down
74 changes: 74 additions & 0 deletions test/NodeGuard.Tests/Jobs/ChannelMonitorJobTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,78 @@ public async Task RecoverChannelInConfirmationPendingStatus_StuckRequestFound()
updatedRequest.Status.Should().Be(ChannelOperationRequestStatus.OnChainConfirmed);
updatedRequest.ChannelId.Should().Be(channel1.Id);
}

[Fact]
public async Task MarkClosedChannelsAsClosed_ChannelsIsNull()
{
// Arrange
var logger = new Mock<ILogger<ChannelMonitorJob>>();
var dbContextFactory = SetupDbContextFactory();
await using var context = await dbContextFactory.Object.CreateDbContextAsync();

var channel1 = new Channel()
{
Id = 4,
FundingTx = "a2dffe0545ae0ce9091949477a9a7d91bb9478eb054fd9fa142e73562287ca4e",
Status = Channel.ChannelStatus.Open
};
await context.Channels.AddAsync(channel1);
await context.SaveChangesAsync();

var channelMonitorJob = new ChannelMonitorJob(logger.Object, dbContextFactory.Object, null, null, null);

var source = new Node() { Id = 3 };
// Act
var act = () => channelMonitorJob.MarkClosedChannelsAsClosed(source, null);

// Assert
await using var newContext = await dbContextFactory.Object.CreateDbContextAsync();
await act.Should().NotThrowAsync();
var existingChannel = await newContext.Channels.FirstAsync();
existingChannel.Status.Should().Be(Channel.ChannelStatus.Open);
}

[Fact]
public async Task MarkClosedChannelsAsClosed_ChannelsIsClosed()
{
// Arrange
var logger = new Mock<ILogger<ChannelMonitorJob>>();
var dbContextFactory = SetupDbContextFactory();
await using var context = await dbContextFactory.Object.CreateDbContextAsync();

var channel1 = new Channel()
{
Id = 4,
ChanId = 2,
FundingTx = "a2dffe0545ae0ce9091949477a9a7d91bb9478eb054fd9fa142e73562287ca4e",
Status = Channel.ChannelStatus.Open,
SourceNodeId = 3
};
var channel2 = new Channel()
{
Id = 2,
ChanId = 3,
FundingTx = "03c63200efc79c6675bd9e3f051a7b4d7e512a7594d1fec64a40e6f1f93a2b2927",
Status = Channel.ChannelStatus.Open,
SourceNodeId = 3
};
await context.Channels.AddAsync(channel1);
await context.Channels.AddAsync(channel2);
await context.SaveChangesAsync();

var channelMonitorJob = new ChannelMonitorJob(logger.Object, dbContextFactory.Object, null, null, null);

var source = new Node() { Id = 3 };
var channelsList = new List<Lnrpc.Channel>() { new Lnrpc.Channel() { ChanId = 2, Active = true } };
// Act
var act = () => channelMonitorJob.MarkClosedChannelsAsClosed(source, channelsList);

// Assert
await using var newContext = await dbContextFactory.Object.CreateDbContextAsync();
await act.Should().NotThrowAsync();
var existingChannel1 = await newContext.Channels.FirstAsync();
existingChannel1.Status.Should().Be(Channel.ChannelStatus.Open);
var existingChannel2 = await newContext.Channels.LastAsync();
existingChannel2.Status.Should().Be(Channel.ChannelStatus.Closed);
}
}
8 changes: 4 additions & 4 deletions test/NodeGuard.Tests/Services/LightningServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,7 @@ public async Task GetChannelsStatus_SourceNodeIsManaged_SourceIsInitiator()
var lightningService = new LightningService(null, null, nodeRepository.Object, null, null, null, null, null ,null, lightningClientService.Object);

// Act
var channelStatus = await lightningService.GetChannelsStatus();
var channelStatus = await lightningService.GetChannelsState();

// Assert
channelStatus[0].LocalBalance.Should().Be(500);
Expand Down Expand Up @@ -1659,7 +1659,7 @@ public async Task GetChannelsStatus_SourceNodeIsManaged_SourceIsNotInitiator()
var lightningService = new LightningService(null, null, nodeRepository.Object, null, null, null, null, null ,null, lightningClientService.Object);

// Act
var channelStatus = await lightningService.GetChannelsStatus();
var channelStatus = await lightningService.GetChannelsState();

// Assert
channelStatus[0].LocalBalance.Should().Be(500);
Expand Down Expand Up @@ -1726,7 +1726,7 @@ public async Task GetChannelsStatus_BothNodesAreManaged_SourceIsInitiator()
var lightningService = new LightningService(null, null, nodeRepository.Object, null, null, null, null, null ,null, lightningClientService.Object);

// Act
var channelStatus = await lightningService.GetChannelsStatus();
var channelStatus = await lightningService.GetChannelsState();

// Assert
channelStatus[0].LocalBalance.Should().Be(500);
Expand Down Expand Up @@ -1793,7 +1793,7 @@ public async Task GetChannelsStatus_BothNodesAreManaged_SourceIsNotInitiator()
var lightningService = new LightningService(null, null, nodeRepository.Object, null, null, null, null, null ,null, lightningClientService.Object);

// Act
var channelStatus = await lightningService.GetChannelsStatus();
var channelStatus = await lightningService.GetChannelsState();

// Assert
channelStatus[0].LocalBalance.Should().Be(500);
Expand Down

0 comments on commit 695b2b7

Please sign in to comment.