Skip to content

Commit

Permalink
Data model for freezing UTXOs (#376)
Browse files Browse the repository at this point in the history
* feat: add address, isFrozen and tag

* feat: filter frozen utxos

* feat: utxo migration

* feat: add postgres for nbxplorer

* fix: add migration for optional address and tag

* feat: RegisterUTXOs job

* feat: add wallet to FMUTXO

* feat(FMUTXO.cs): add IsSpent property to determine if UTXO is spent based on channel and withdrawal statuses

* feat(FMUTXO.cs): add NBitcoin and NBXplorer dependencies for UTXO model enhancements

feat(FMUTXO.cs): implement Equals method for UTXO comparison with NBitcoin types

refactor(RegisterUTXOsJob.cs): adjust equality check to use modified Equals method in FMUTXO model for clarity and consistency

* feat: consolidate migrations

* fix: migration

* refactor(ChannelOperationRequestRepositoryTests): add additional null parameter to constructor calls to align with updated constructor signature

* Bump deps

* Fix problem with new change in nbxplorer when tracking btc addresses

* feat: rebuild of the migration

* EF Core already does execute the migration in a transaction scope, this was giving a race condition when generating the db (#375)

* feat: changes for deps update

* Fix AddRangeAsync not using a generic function type making EF Core fail to recognize the entities when saving a list

* Fix  RegisterUTXOsJob to use WalletId instead of Wallet property

* fix: rollback changes

* feat(FMUTXO.cs): add Blazorise.Extensions for enhanced functionality
refactor(FMUTXO.cs): improve IsSpent property logic to handle null collections
feat(FUTXORepository.cs): include Tags in FMUTXOs query to provide more detailed data

* fix: remove error and set the creation time and the update time

* refactor(FUTXORepository.cs): optimize UTXO retrieval logic to enhance performance and accuracy
feat(RegisterUTXOsJob.cs): log information after adding new UTXOs to enhance traceability
fix(Constants.cs, launchSettings.json): align REGISTER_UTXOS_CRON schedule across configurations for consistency

* fix: rollback the port

* fix: remove isSpent

* refactor(FUTXORepository): replace GetFromUTXOs with GetByOutpoint for specific UTXO retrieval
feat(CoinSelectionService): update UTXO selection logic to handle new and existing UTXOs
feat(CoinSelectionService): enhance error handling and logging for UTXO operations
feat(Interfaces): update IFMUTXORepository interface to reflect method changes
style(CoinSelectionService): add Blazorise dependencies for UI enhancements

* fix(docker-compose.yml): change PostgreSQL service port mapping to avoid port conflicts

* fix: fix method type

* fix: remove unused imports

* fix: comments

* feat: UTXOTag in another file

* fix: set to dbtrie again

* feat: explanation of the job

* Update src/Services/CoinSelectionService.cs

Co-authored-by: Rodrigo <[email protected]>

* Update src/Services/CoinSelectionService.cs

Co-authored-by: Rodrigo <[email protected]>

* feat(RegisterUTXOsJob.cs): skip processing wallets without derivation strategy

refactor(RegisterUTXOsJob.cs): improve error logging format for consistency

style(RegisterUTXOsJob.cs): change log level from Information to Debug for UTXO processing logs to reduce log verbosity

* fix: tags in the relations

* fix: add throw

* chore: comments

* refactor: remove job to add utxos

* refactor: remove properties to rollback changes

* refactor: remove unused methods

* chore(FMUTXO.cs): remove unused using directives to clean up code

* style(FUTXORepository.cs): remove extra whitespace for cleaner code format

* refactor(Constants.cs): remove REGISTER_UTXOS_CRON constant and its environment variable handling as it is no longer used in the application

* style(FMUTXO.cs): remove extra whitespace for cleaner code formatting

* refactor(FMUTXO.cs): remove unused NBitcoin and NBXplorer imports to clean up code

refactor(FMUTXO.cs): remove redundant Equals method that compares UTXO objects, simplifying the model

* refactor: remove logic for FMUTXOs

* refactor: remove spaces

* refactor: rollback logic changes

* style(CoinSelectionService.cs): add missing line break for better code readability

* refactor: remove migration

* feat: utxotag repository

* refactor(UTXOTag.cs): remove FMUTXO navigation properties to decouple UTXOTag from FMUTXO model

* feat(IUTXOTagRepository, UTXOTagRepository): add GetTagByKeyAndOutpoint method

WHY: Enhance querying capabilities by allowing retrieval of UTXOTag based on both key and outpoint, facilitating more specific data access patterns.

* style(UTXOTagRepository.cs): fix indentation for better code readability

* refactor(UTXOTag): make Outpoint property non-nullable to enforce data integrity

refactor(IUTXOTagRepository): rename GetTagByKeyAndOutpoint to GetByKeyAndOutpoint for consistency

feat(IUTXOTagRepository): add GetByKeyValue method to query UTXOTags by key and value

* feat: exclude frozen utxos

* fix: add repository

* feat: migration

* refactor(NodeGuardServiceTests.cs): add missing null parameter to constructors for consistency
refactor(BitcoinServiceTests.cs): add missing null parameter in CoinSelectionService constructor for completeness

* feat: add unique constraint for key outpoint in the utxotag table

* test: fix tests and add one more

* Update src/Helpers/Constants.cs

Co-authored-by: José A.P <[email protected]>

---------

Co-authored-by: José A.P <[email protected]>
Co-authored-by: Rodrigo <[email protected]>
  • Loading branch information
3 people authored Jul 1, 2024
1 parent 270bae3 commit d3d3b1f
Show file tree
Hide file tree
Showing 20 changed files with 1,671 additions and 48 deletions.
19 changes: 17 additions & 2 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: '3.4'
services:
nodeguard_postgres:
container_name: nodeguard_postgres
image: postgres:13
image: postgres:16
restart: always
environment:
POSTGRES_DB: nodeguard
Expand All @@ -29,7 +29,7 @@ services:
NBXPLORER_SIGNALFILESDIR: /datadir
#Keeping dbtrie for dev until it is fully removed since we would need to modify nbxplorer docker image to wait for the db to be ready
NBXPLORER_DBTRIE: 1
# NBXPLORER_POSTGRES: Host=nodeguard_postgres;Port=5432;Database=nbxplorer;Username=rw_dev;Password=rw_dev
# NBXPLORER_POSTGRES: Host=nbxplorer_postgres;Port=5432;Database=nbxplorer;Username=rw_dev;Password=rw_dev
NBXPLORER_CHAINS: "btc"
NBXPLORER_BTCRPCUSER: "polaruser"
NBXPLORER_BTCRPCPASSWORD: "polarpass"
Expand All @@ -38,11 +38,26 @@ services:
command: ["--noauth"]
volumes:
- "bitcoin_datadir:/root/.bitcoin"

nbxplorer_postgres:
container_name: nbxplorer_postgres
image: postgres:16
restart: always
environment:
POSTGRES_DB: nbxplorer
POSTGRES_USER: rw_dev
POSTGRES_PASSWORD: rw_dev
TZ: Europe/Madrid
volumes:
- nbxplorer_postgres_data:/var/lib/postgresql/data
ports:
- 35432:5432



volumes:
nodeguard_postgres_data:
bitcoin_datadir:
nbxplorer_datadir:
nbxplorer_postgres_data:
nodeguard_data_keys_dir:
2 changes: 2 additions & 0 deletions src/Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
public DbSet<InternalWallet> InternalWallets { get; set; }

public DbSet<FMUTXO> FMUTXOs { get; set; }

public DbSet<UTXOTag> UTXOTags { get; set; }

public DbSet<LiquidityRule> LiquidityRules { get; set; }

Expand Down
3 changes: 3 additions & 0 deletions src/Data/Models/FMUTXO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ public class FMUTXO : Entity, IEquatable<FMUTXO>

#region Relationships

// M-N Because if the UTXO is used in a request that gets cancelled,
// the UTXO should be unlocked and assigned to another request
public List<ChannelOperationRequest> ChannelOperationRequests { get; set; }

// Idem as ChannelOperationRequests
public List<WalletWithdrawalRequest> WalletWithdrawalRequests { get; set; }

#endregion Relationships
Expand Down
11 changes: 11 additions & 0 deletions src/Data/Models/UTXOTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace NodeGuard.Data.Models;

public class UTXOTag : Entity
{
public string Key { get; set; }

public string Value { get; set; }

// Outpoint of the UTXO in format "hash-index"
public string Outpoint { get; set; }
}
2 changes: 1 addition & 1 deletion src/Data/Repositories/FUTXORepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
*/

using NodeGuard.Data.Models;
using NodeGuard.Data.Models;
using NodeGuard.Data.Repositories.Interfaces;
using Microsoft.EntityFrameworkCore;

Expand Down
2 changes: 1 addition & 1 deletion src/Data/Repositories/Interfaces/IFMUTXORepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
*/

using NodeGuard.Data.Models;
using NodeGuard.Data.Models;

namespace NodeGuard.Data.Repositories.Interfaces;

Expand Down
2 changes: 1 addition & 1 deletion src/Data/Repositories/Interfaces/IRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public interface IRepository<T>
/// </summary>
/// <param name="T"></param>
/// <returns>A tuple (bool, string). The bool represents the call success and the string any possible message.</returns>
public Task<(bool, string?)> AddRangeAsync(List<T> T, ApplicationDbContext applicationDbContext);
public Task<(bool, string?)> AddRangeAsync<T>(List<T> entities, ApplicationDbContext applicationDbContext) where T : class;

/// <summary>
///Removes an existing entity of <see cref="IRepository{T}"></see>
Expand Down
22 changes: 22 additions & 0 deletions src/Data/Repositories/Interfaces/IUTXOTagRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using NodeGuard.Data.Models;

namespace NodeGuard.Data.Repositories.Interfaces;

public interface IUTXOTagRepository
{
Task<UTXOTag?> GetByOutpoint(string outpoint);

Task<UTXOTag?> GetByKeyAndOutpoint(string key, string outpoint);

Task<List<UTXOTag>> GetByKeyValue(string key, string value);

Task<(bool, string?)> AddAsync(UTXOTag type);

Task<(bool, string?)> AddRangeAsync(List<UTXOTag> type);

(bool, string?) Remove(UTXOTag type);

(bool, string?) RemoveRange(List<UTXOTag> types);

(bool, string?) Update(UTXOTag type);
}
7 changes: 4 additions & 3 deletions src/Data/Repositories/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,20 @@ public async Task<List<T>> GetAll(ApplicationDbContext applicationDbContext)
return (rowsChanged, null);
}

public async Task<(bool, string?)> AddRangeAsync(List<T> T, ApplicationDbContext applicationDbContext)
public async Task<(bool, string?)> AddRangeAsync<T>(List<T> entities, ApplicationDbContext applicationDbContext) where T : class
{
if (T == null) throw new ArgumentNullException(nameof(T));
if (entities == null) throw new ArgumentNullException(nameof(entities));

var rowsChanged = false;
try
{
await applicationDbContext.AddRangeAsync(T);
await applicationDbContext.AddRangeAsync(entities);
rowsChanged = await applicationDbContext.SaveChangesAsync() > 0;
}
catch (Exception e)
{
_logger.LogError(e, "Error on repository");
return (false, e.Message);
}

return (rowsChanged, null);
Expand Down
82 changes: 82 additions & 0 deletions src/Data/Repositories/UTXOTagRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Microsoft.EntityFrameworkCore;
using NodeGuard.Data.Models;
using NodeGuard.Data.Repositories.Interfaces;

namespace NodeGuard.Data.Repositories;

public class UTXOTagRepository : IUTXOTagRepository
{
private readonly IRepository<UTXOTag> _repository;
private readonly ILogger<UTXOTagRepository> _logger;
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;

public UTXOTagRepository(IRepository<UTXOTag> repository,
ILogger<UTXOTagRepository> logger,
IDbContextFactory<ApplicationDbContext> dbContextFactory)
{
_repository = repository;
_logger = logger;
_dbContextFactory = dbContextFactory;
}

public async Task<UTXOTag?> GetByOutpoint(string outpoint)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

return await applicationDbContext.UTXOTags.FirstOrDefaultAsync(x => x.Outpoint == outpoint);
}

public async Task<UTXOTag?> GetByKeyAndOutpoint(string key, string outpoint)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

return await applicationDbContext.UTXOTags.FirstOrDefaultAsync(x => x.Key == key && x.Outpoint == outpoint);
}

public async Task<List<UTXOTag>> GetByKeyValue(string key, string value)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

return await applicationDbContext.UTXOTags
.Where(x => x.Key == key && x.Value == value).ToListAsync();
}

public async Task<(bool, string?)> AddAsync(UTXOTag type)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

type.SetCreationDatetime();

return await _repository.AddAsync(type, applicationDbContext);
}

public async Task<(bool, string?)> AddRangeAsync(List<UTXOTag> type)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

return await _repository.AddRangeAsync(type, applicationDbContext);
}

public (bool, string?) Remove(UTXOTag type)
{
using var applicationDbContext = _dbContextFactory.CreateDbContext();

return _repository.Remove(type, applicationDbContext);
}

public (bool, string?) RemoveRange(List<UTXOTag> types)
{
using var applicationDbContext = _dbContextFactory.CreateDbContext();

return _repository.RemoveRange(types, applicationDbContext);
}

public (bool, string?) Update(UTXOTag type)
{
using var applicationDbContext = _dbContextFactory.CreateDbContext();

type.SetUpdateDatetime();

return _repository.Update(type, applicationDbContext);
}
}
4 changes: 3 additions & 1 deletion src/Helpers/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ public class Constants
/// Max ratio of the tx total input sum that could be used as fee
/// </summary>
public static decimal MAX_TX_FEE_RATIO =0.5m;


public const string IsFrozenTag = "frozen";

private static string? GetEnvironmentalVariableOrThrowIfNotTesting(string envVariableName, string? errorMessage = null)
{
// If it is a command from ef or a test, ignore the empty env variables
Expand Down
Loading

0 comments on commit d3d3b1f

Please sign in to comment.