From 721c8bffe02dd2a8651b7a4315add22231ac42a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20A=2EP?= <53834183+Jossec101@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:17:26 +0200 Subject: [PATCH 1/4] refactor(Channels.razor): remove unused import Polly style(Channels.razor): change variable case from _IsUserNodeManager to _isUserNodeManager for consistency refactor(Channels.razor): change ChannelsColumnName enum values to uppercase for better readability refactor(Channels.razor): remove unnecessary type casting in SelectedValue attributes refactor(Channels.razor): change nullable types to non-nullable and vice versa according to their usage refactor(Channels.razor): apply null safety checks and improve variable naming for readability 1. refactor: rename variables to follow underscore prefix naming convention for private fields 2. refactor: add null safety checks before accessing nullable properties or methods 3. refactor: change variable names to uppercase for constants to follow naming conventions 4. refactor: remove unused exception variable in catch block 5. refactor: add explicit type to remoteBalance variable for clarity 6. refactor: add null checks before accessing nullable properties or methods 7. refactor: add null check before accessing _currentLiquidityRule in validation methods 8. refactor: change method parameters to nullable types in filter methods 9. refactor: improve logic in CheckDisableCloseChannelButton method for readability --- src/Pages/Channels.razor | 142 +++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/src/Pages/Channels.razor b/src/Pages/Channels.razor index a735a86a..9c66159b 100644 --- a/src/Pages/Channels.razor +++ b/src/Pages/Channels.razor @@ -2,7 +2,6 @@ @using System.Security.Claims @using Humanizer @using NBitcoin -@using Polly @using Channel = FundsManager.Data.Models.Channel @attribute [Authorize(Roles = "NodeManager")] Active Channels @@ -34,7 +33,7 @@ - @if (_IsUserNodeManager) + @if (_isUserNodeManager) { Manage Close @@ -46,9 +45,9 @@ - + - All @foreach (var node in _nodes) { @@ -65,9 +64,9 @@ - + - All @foreach (var node in _nodes) { @@ -84,7 +83,7 @@ - + All @@ -117,7 +116,7 @@ } - + @StringHelper.TruncateHeadAndTail(context.FundingTx, 5):@context.FundingTxOutputIndex - + @{ var btcAmount = new Money(context.SatsAmount, MoneyUnit.Satoshi).ToUnit(MoneyUnit.BTC); @@ -134,8 +133,8 @@ } - - + + @if (!string.IsNullOrEmpty(context.BtcCloseAddress)) @@ -148,7 +147,7 @@ - + @{ var balance = Task.Run(() => GetPercentageBalance(context)).Result; @@ -170,9 +169,9 @@ - - - + + + @context.ChanId @@ -181,7 +180,7 @@ - + @@ -340,15 +339,15 @@ private List? _channels = new List(); private Channel? _selectedChannel; private bool _modalVisible; - private bool _IsUserNodeManager = false; - private DataGrid _channelsDataGridRef; + private bool _isUserNodeManager; + private DataGrid? _channelsDataGridRef; private Modal? _channelLiquidityModal; private List _availableWallets = new List(); private LiquidityRule? _currentLiquidityRule = new LiquidityRule(); - private Validations _channelManagementValidationsRef; + private Validations? _channelManagementValidationsRef; private string _statusFilter = "Open"; - private int _sourceNodeIdFilter = 0; - private int _destinationNodeIdFilter = 0; + private int _sourceNodeIdFilter; + private int _destinationNodeIdFilter; private List _nodes = new List(); private List _wallets = new List(); private int _filterWalletId; @@ -361,27 +360,27 @@ private decimal _btcPrice; - private ColumnLayout ChannelsColumnLayout; - private Dictionary ChannelsColumns = new(); - private bool columnsLoaded; + private ColumnLayout? _channelsColumnLayout; + private Dictionary _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 _channelsBalance = new(); private DateTimeOffset _lastBalanceUpdate = DateTimeOffset.Now; public abstract class ChannelsColumnName { - public static readonly ColumnDefault SourceNode = new("Source Node"); - public static readonly ColumnDefault DestinationNode = new("Destination Node"); - public static readonly ColumnDefault Status = new("Status"); - public static readonly ColumnDefault OpenedWith = new("Opened With"); - public static readonly ColumnDefault Outpoint = new("Outpoint"); - public static readonly ColumnDefault Capacity = new("Capacity (BTC)"); - public static readonly ColumnDefault Private = new("Private"); - public static readonly ColumnDefault CloseAddress = new("Close Address"); - public static readonly ColumnDefault ChannelBalance = new("Channel Balance"); - public static readonly ColumnDefault CreationDate = new("Creation Date"); - public static readonly ColumnDefault UpdateDate = new("Update Date"); - public static readonly ColumnDefault ChannelId = new("Channel Id"); + public static readonly ColumnDefault SOURCE_NODE = new("Source Node"); + public static readonly ColumnDefault DESTINATION_NODE = new("Destination Node"); + public static readonly ColumnDefault STATUS = new("Status"); + public static readonly ColumnDefault OPENED_WITH = new("Opened With"); + public static readonly ColumnDefault OUTPOINT = new("Outpoint"); + public static readonly ColumnDefault CAPACITY = new("Capacity (BTC)"); + public static readonly ColumnDefault PRIVATE = new("Private"); + public static readonly ColumnDefault CLOSE_ADDRESS = new("Close Address"); + public static readonly ColumnDefault CHANNEL_BALANCE = new("Channel Balance"); + public static readonly ColumnDefault CREATION_DATE = new("Creation Date"); + public static readonly ColumnDefault UPDATE_DATE = new("Update Date"); + public static readonly ColumnDefault CHANNEL_ID = new("Channel Id"); } protected override async Task OnInitializedAsync() @@ -396,7 +395,7 @@ await FetchData(); if (ClaimsPrincipal != null && ClaimsPrincipal.IsInRole(ApplicationUserRole.NodeManager.ToString())) { - _IsUserNodeManager = true; + _isUserNodeManager = true; } } } @@ -404,16 +403,19 @@ private async Task FetchData() { _availableWallets = await WalletRepository.GetAvailableWallets(); - _channels = await ChannelRepository.GetAllManagedByUserNodes(LoggedUser.Id); + if (LoggedUser?.Id != null) + { + _channels = await ChannelRepository.GetAllManagedByUserNodes(LoggedUser.Id); + } _nodes = await NodeRepository.GetAll(); _wallets = await WalletRepository.GetAll(); - _channelsDataGridRef.FilterData(); + _channelsDataGridRef?.FilterData(); _channelsBalance = await LightningService.GetChannelsBalance(); } protected override async Task OnAfterRenderAsync(bool firstRender) { - if (!firstRender && !columnsLoaded) + if (!firstRender && !_columnsLoaded) { await LoadColumnLayout(); } @@ -421,19 +423,19 @@ private async Task LoadColumnLayout() { - ChannelsColumns = await LocalStorageService.LoadStorage(nameof(ChannelsColumnName), ColumnHelpers.GetColumnsDictionary()); - columnsLoaded = true; + _channelsColumns = await LocalStorageService.LoadStorage(nameof(ChannelsColumnName), ColumnHelpers.GetColumnsDictionary()); + _columnsLoaded = true; StateHasChanged(); } - private async Task ShowConfirmedClose(Channel channel, bool forceClose = false) + private async Task ShowConfirmedClose(Channel? channel, bool forceClose = false) { if (await MessageService.Confirm($"Are you sure you want to {(forceClose ? "force " : string.Empty)}close this channel?", "Confirmation")) { if (channel != null) { - channel.DestinationNode = await NodeRepository.GetById(channel.DestinationNodeId); - channel.SourceNode = await NodeRepository.GetById(channel.SourceNodeId); + channel.DestinationNode = await NodeRepository.GetById(channel.DestinationNodeId) ?? throw new InvalidOperationException(); + channel.SourceNode = await NodeRepository.GetById(channel.SourceNodeId) ?? throw new InvalidOperationException(); var result = await ChannelRepository.SafeRemove(channel, forceClose); if (!result.Item1) @@ -492,7 +494,7 @@ var capacity = values.Item2 + values.Item3; // Initialize the remote balance - var remoteBalance = 0L; + long remoteBalance; // Check if the source node ID of the channel is the same as the managed node ID // or if the source node is not managed @@ -517,7 +519,7 @@ return Convert.ToInt32(result); } - catch (Exception e) + catch (Exception) { ToastService.ShowError($"Channel balance for channel id:{channel.Id} could not be retrieved"); return -1; @@ -526,29 +528,32 @@ private async Task OnSelectedWalletChanged(int arg) { - _currentLiquidityRule.WalletId = arg; + if (_currentLiquidityRule != null) _currentLiquidityRule.WalletId = arg; } private async Task CloseChannelManagementModal() { - await _channelLiquidityModal?.Close(CloseReason.UserClosing); + if (_channelLiquidityModal != null) await _channelLiquidityModal.Close(CloseReason.UserClosing); await ClearManagementModal(); await FetchData(); } private async Task SaveAndCloseChannelManagementModal() { - if (await _channelManagementValidationsRef.ValidateAll()) + if (_channelManagementValidationsRef != null && await _channelManagementValidationsRef.ValidateAll()) { //Save the channel - var updateResult = ChannelRepository.Update(_selectedChannel); - if (!updateResult.Item1) - { - ToastService.ShowError("Something went wrong"); - } - else + if (_selectedChannel != null) { - ToastService.ShowSuccess("Channel updated successfully"); + var updateResult = ChannelRepository.Update(_selectedChannel); + if (!updateResult.Item1) + { + ToastService.ShowError("Something went wrong"); + } + else + { + ToastService.ShowSuccess("Channel updated successfully"); + } } //Save the liquidity rule if the liquidity rule id is below or equal zero @@ -614,7 +619,7 @@ NodeId = node.Id }; - await _channelLiquidityModal?.Show(); + if (_channelLiquidityModal != null) await _channelLiquidityModal.Show(); } private async Task OnEnableLiquidityMgnmtChanged(bool enabledLiquidityMgnmt) @@ -647,6 +652,7 @@ private void ValidateTargetBalance(ValidatorEventArgs arg1) { + if(_currentLiquidityRule == null) return; //Default validation status arg1.Status = ValidationStatus.Success; @@ -674,6 +680,7 @@ private void ValidateRemoteBalance(ValidatorEventArgs arg1) { + if(_currentLiquidityRule == null) return; //Default validation status arg1.Status = ValidationStatus.Success; @@ -710,6 +717,7 @@ private void ValidateLocalBalance(ValidatorEventArgs arg1) { + if (_currentLiquidityRule == null) return; //Default validation status arg1.Status = ValidationStatus.Success; //If the minimum remote balance is 0 this cannot be 0 @@ -743,7 +751,7 @@ } } - private bool OnStatusFilter(object itemValue, object searchValue) + private bool OnStatusFilter(object? itemValue, object? searchValue) { //If searchValue is null, we set it to the filter initial value in the field searchValue ??= _statusFilter; @@ -763,7 +771,7 @@ return searchValue == null || (int) searchValue == 0 || (int) itemValue == (int) searchValue; } - private bool OnSourceNodeIdFilter(object itemValue, object searchValue) + private bool OnSourceNodeIdFilter(object? itemValue, object? searchValue) { //If searchValue is null, we set it to the filter initial value in the field searchValue ??= _sourceNodeIdFilter; @@ -775,7 +783,7 @@ return true; } - private bool OnDestinationNodeIdFilter(object itemValue, object searchValue) + private bool OnDestinationNodeIdFilter(object? itemValue, object? searchValue) { //If searchValue is null, we set it to the filter initial value in the field searchValue ??= _destinationNodeIdFilter; @@ -807,7 +815,11 @@ private bool CheckDisableCloseChannelButton(Channel channel) { var lastRequest = channel.ChannelOperationRequests.LastOrDefault(); - if (lastRequest == null && channel.CreatedByNodeGuard == false) return false; + + // If the channel is not created by the node guard, we dont disable the button + if (lastRequest == null || channel.CreatedByNodeGuard == false) return false; + + // If the channel is created by the node guard, we disable the button if the channel is closed or if the last request is a close request and is confirmed or is pending confirmation return channel.Status == Channel.ChannelStatus.Closed || (lastRequest.RequestType == OperationRequestType.Close && lastRequest.Status == ChannelOperationRequestStatus.OnChainConfirmed || lastRequest.Status == ChannelOperationRequestStatus.OnChainConfirmationPending); @@ -820,11 +832,11 @@ private bool IsColumnVisible(ColumnDefault column) { - if (ChannelsColumnLayout == null) + if (_channelsColumnLayout == null) { return true; } - return ChannelsColumnLayout.IsColumnVisible(column); + return _channelsColumnLayout.IsColumnVisible(column); } } \ No newline at end of file From e960dd0d359fe608e20bb0aa2a6bf3c8bd740347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20A=2EP?= <53834183+Jossec101@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:35:38 +0200 Subject: [PATCH 2/4] feat(Channels.razor): add default LiquidityRule when none exists for selected channel This change ensures that a default LiquidityRule is created when none exists for the selected channel. This is done to prevent potential null reference exceptions and to provide a sensible default for the channel's liquidity management. --- src/Pages/Channels.razor | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Pages/Channels.razor b/src/Pages/Channels.razor index 9c66159b..f0ff1765 100644 --- a/src/Pages/Channels.razor +++ b/src/Pages/Channels.razor @@ -610,14 +610,14 @@ var node = string.IsNullOrEmpty(sourceNode.ChannelAdminMacaroon) ? destinationNode : sourceNode; //If there is a liquidity rule for this channel, we load it, the first one - _currentLiquidityRule = _selectedChannel?.LiquidityRules.FirstOrDefault() ?? new LiquidityRule + _currentLiquidityRule = _selectedChannel?.LiquidityRules.FirstOrDefault()?? new LiquidityRule { MinimumLocalBalance = 20, MinimumRemoteBalance = 80, RebalanceTarget = 50, ChannelId = channel.Id, NodeId = node.Id - }; + };; if (_channelLiquidityModal != null) await _channelLiquidityModal.Show(); } @@ -634,7 +634,15 @@ } else { - _currentLiquidityRule = _selectedChannel.LiquidityRules.FirstOrDefault(); + var node = string.IsNullOrEmpty(_selectedChannel.SourceNode.ChannelAdminMacaroon) ? _selectedChannel.DestinationNode : _selectedChannel.SourceNode; + _currentLiquidityRule = _selectedChannel.LiquidityRules.FirstOrDefault() ?? new LiquidityRule + { + MinimumLocalBalance = 20, + MinimumRemoteBalance = 80, + RebalanceTarget = 50, + ChannelId = _selectedChannel.Id, + NodeId = node.Id + }; } var updateResult = ChannelRepository.Update(_selectedChannel); From 7742494d0647ae9dfc9ef6b4a56ae065abcf7216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20A=2EP?= <53834183+Jossec101@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:24:19 +0200 Subject: [PATCH 3/4] Update src/Pages/Channels.razor Co-authored-by: Rodrigo <39995243+RodriFS@users.noreply.github.com> --- src/Pages/Channels.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pages/Channels.razor b/src/Pages/Channels.razor index f0ff1765..9088d840 100644 --- a/src/Pages/Channels.razor +++ b/src/Pages/Channels.razor @@ -617,7 +617,7 @@ RebalanceTarget = 50, ChannelId = channel.Id, NodeId = node.Id - };; + }; if (_channelLiquidityModal != null) await _channelLiquidityModal.Show(); } From 05e434b7c5509ce2ed45c2ce5dca67f7cc3d05cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20A=2EP?= <53834183+Jossec101@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:24:27 +0200 Subject: [PATCH 4/4] Update src/Pages/Channels.razor Co-authored-by: Rodrigo <39995243+RodriFS@users.noreply.github.com> --- src/Pages/Channels.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pages/Channels.razor b/src/Pages/Channels.razor index 9088d840..a15f578c 100644 --- a/src/Pages/Channels.razor +++ b/src/Pages/Channels.razor @@ -610,7 +610,7 @@ var node = string.IsNullOrEmpty(sourceNode.ChannelAdminMacaroon) ? destinationNode : sourceNode; //If there is a liquidity rule for this channel, we load it, the first one - _currentLiquidityRule = _selectedChannel?.LiquidityRules.FirstOrDefault()?? new LiquidityRule + _currentLiquidityRule = _selectedChannel?.LiquidityRules.FirstOrDefault() ?? new LiquidityRule { MinimumLocalBalance = 20, MinimumRemoteBalance = 80,