mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-24 06:47:50 +01:00
Fix server event communication
This commit is contained in:
parent
d18e8d2eaf
commit
8017493ff5
12 changed files with 82 additions and 109 deletions
|
@ -3,26 +3,14 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Lightning;
|
||||
using NBitcoin;
|
||||
using LightningPayment = BTCPayApp.CommonServer.Models.LightningPayment;
|
||||
|
||||
namespace BTCPayApp.CommonServer;
|
||||
|
||||
|
||||
public class TransactionDetectedRequest
|
||||
{
|
||||
public string Identifier { get; set; }
|
||||
public string TxId { get; set; }
|
||||
public string[] SpentScripts { get; set; }
|
||||
public string[] ReceivedScripts { get; set; }
|
||||
public bool Confirmed { get; set; }
|
||||
}
|
||||
|
||||
|
||||
//methods available on the hub in the client
|
||||
public interface IBTCPayAppHubClient
|
||||
{
|
||||
Task NotifyServerEvent(IServerEvent ev);
|
||||
Task NotifyServerEvent(ServerEvent ev);
|
||||
Task NotifyNetwork(string network);
|
||||
Task NotifyServerNode(string nodeInfo);
|
||||
Task TransactionDetected(TransactionDetectedRequest request);
|
||||
|
@ -36,6 +24,28 @@ public interface IBTCPayAppHubClient
|
|||
Task<PayResponse> PayInvoice(string bolt11, long? amountMilliSatoshi);
|
||||
}
|
||||
|
||||
//methods available on the hub in the server
|
||||
public interface IBTCPayAppHubServer
|
||||
{
|
||||
Task<bool> IdentifierActive(string group, bool active);
|
||||
|
||||
Task<Dictionary<string,string>> Pair(PairRequest request);
|
||||
Task<AppHandshakeResponse> Handshake(AppHandshake request);
|
||||
Task<bool> BroadcastTransaction(string tx);
|
||||
Task<decimal> GetFeeRate(int blockTarget);
|
||||
Task<BestBlockResponse> GetBestBlock();
|
||||
Task<string> GetBlockHeader(string hash);
|
||||
|
||||
Task<TxInfoResponse> FetchTxsAndTheirBlockHeads(string[] txIds);
|
||||
Task<string> DeriveScript(string identifier);
|
||||
Task TrackScripts(string identifier, string[] scripts);
|
||||
Task<string> UpdatePsbt(string[] identifiers, string psbt);
|
||||
Task<CoinResponse[]> GetUTXOs(string[] identifiers);
|
||||
Task<Dictionary<string, TxResp[]>> GetTransactions(string[] identifiers);
|
||||
|
||||
Task SendPaymentUpdate(string identifier, LightningPayment lightningPayment);
|
||||
}
|
||||
|
||||
public interface IServerEvent
|
||||
{
|
||||
public string Type { get; }
|
||||
|
@ -63,27 +73,13 @@ public record TxResp(long Confirmations, long? Height, decimal BalanceChange, Da
|
|||
}
|
||||
}
|
||||
|
||||
//methods available on the hub in the server
|
||||
public interface IBTCPayAppHubServer
|
||||
public class TransactionDetectedRequest
|
||||
{
|
||||
Task<bool> IdentifierActive(string group, bool active);
|
||||
|
||||
Task<Dictionary<string,string>> Pair(PairRequest request);
|
||||
Task<AppHandshakeResponse> Handshake(AppHandshake request);
|
||||
Task<bool> BroadcastTransaction(string tx);
|
||||
Task<decimal> GetFeeRate(int blockTarget);
|
||||
Task<BestBlockResponse> GetBestBlock();
|
||||
Task<string> GetBlockHeader(string hash);
|
||||
|
||||
Task<TxInfoResponse> FetchTxsAndTheirBlockHeads(string[] txIds);
|
||||
Task<string> DeriveScript(string identifier);
|
||||
Task TrackScripts(string identifier, string[] scripts);
|
||||
Task<string> UpdatePsbt(string[] identifiers, string psbt);
|
||||
Task<CoinResponse[]> GetUTXOs(string[] identifiers);
|
||||
Task<Dictionary<string, TxResp[]>> GetTransactions(string[] identifiers);
|
||||
|
||||
|
||||
Task SendPaymentUpdate(string identifier, LightningPayment lightningPayment);
|
||||
public string Identifier { get; set; }
|
||||
public string TxId { get; set; }
|
||||
public string[] SpentScripts { get; set; }
|
||||
public string[] ReceivedScripts { get; set; }
|
||||
public bool Confirmed { get; set; }
|
||||
}
|
||||
|
||||
public class CoinResponse
|
||||
|
@ -108,15 +104,12 @@ public class TransactionResponse
|
|||
public string? BlockHash { get; set; }
|
||||
public int? BlockHeight { get; set; }
|
||||
public string Transaction { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class BestBlockResponse
|
||||
{
|
||||
public required string BlockHash { get; set; }
|
||||
public required int BlockHeight { get; set; }
|
||||
|
||||
public string BlockHeader { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
@ -101,8 +101,6 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
private readonly ILogger<BTCPayAppHub> _logger;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
|
||||
public BTCPayAppHub(BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
NBXplorerDashboard nbXplorerDashboard,
|
||||
|
@ -111,7 +109,6 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
IFeeProviderFactory feeProviderFactory,
|
||||
ILogger<BTCPayAppHub> logger,
|
||||
StoreRepository storeRepository,
|
||||
EventAggregator eventAggregator,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
|
@ -122,7 +119,6 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
|
@ -144,31 +140,14 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, userStore.Id);
|
||||
}
|
||||
|
||||
_compositeDisposable = new CompositeDisposable();
|
||||
_compositeDisposable.Add(_eventAggregator.SubscribeAsync<UserStoreAddedEvent>(StoreUserAddedEvent));
|
||||
_compositeDisposable.Add(_eventAggregator.SubscribeAsync<UserStoreRemovedEvent>(StoreUserRemovedEvent));
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
_compositeDisposable?.Dispose();
|
||||
await _appState.Disconnected(Context.ConnectionId);
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
private async Task StoreUserAddedEvent(UserStoreAddedEvent arg)
|
||||
{
|
||||
// TODO: Groups and COntext are not accessible here
|
||||
// await Groups.AddToGroupAsync(Context.ConnectionId, arg.StoreId);
|
||||
}
|
||||
|
||||
private async Task StoreUserRemovedEvent(UserStoreRemovedEvent arg)
|
||||
{
|
||||
// TODO: Groups and COntext are not accessible here
|
||||
// await Groups.RemoveFromGroupAsync(Context.ConnectionId, arg.UserId);
|
||||
}
|
||||
|
||||
public async Task<bool> BroadcastTransaction(string tx)
|
||||
{
|
||||
var explorerClient = _explorerClientProvider.GetExplorerClient( _btcPayNetworkProvider.BTC);
|
||||
|
@ -198,8 +177,8 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
var explorerClient = _explorerClientProvider.GetExplorerClient( _btcPayNetworkProvider.BTC);
|
||||
var bcInfo = await explorerClient.RPCClient.GetBlockchainInfoAsyncEx();
|
||||
var bh = await GetBlockHeader(bcInfo.BestBlockHash.ToString());
|
||||
_logger.LogInformation($"Getting best block done");
|
||||
return new BestBlockResponse()
|
||||
_logger.LogInformation("Getting best block done");
|
||||
return new BestBlockResponse
|
||||
{
|
||||
BlockHash = bcInfo.BestBlockHash.ToString(),
|
||||
BlockHeight = bcInfo.Blocks,
|
||||
|
@ -209,7 +188,6 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
|
||||
public async Task<string> GetBlockHeader(string hash)
|
||||
{
|
||||
|
||||
var explorerClient = _explorerClientProvider.GetExplorerClient( _btcPayNetworkProvider.BTC);
|
||||
var bh = await explorerClient.RPCClient.GetBlockHeaderAsync(uint256.Parse(hash));
|
||||
return Convert.ToHexString(bh.ToBytes()).ToLower();
|
||||
|
@ -217,12 +195,10 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
|
||||
public async Task<TxInfoResponse> FetchTxsAndTheirBlockHeads(string[] txIds)
|
||||
{
|
||||
|
||||
var cancellationToken = Context.ConnectionAborted;
|
||||
var explorerClient = _explorerClientProvider.GetExplorerClient( _btcPayNetworkProvider.BTC);
|
||||
var uints = txIds.Select(uint256.Parse).ToArray();
|
||||
var txsFetch = await Task.WhenAll(uints.Select(
|
||||
uint256 =>
|
||||
var txsFetch = await Task.WhenAll(uints.Select(uint256 =>
|
||||
explorerClient.GetTransactionAsync(uint256, cancellationToken)));
|
||||
|
||||
var batch = explorerClient.RPCClient.PrepareBatch();
|
||||
|
@ -231,14 +207,12 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
batch.GetBlockHeaderAsync(result.BlockId, cancellationToken));
|
||||
await batch.SendBatchAsync(cancellationToken);
|
||||
|
||||
|
||||
|
||||
var headerToHeight = (await Task.WhenAll(headersTask.Values)).ToDictionary(header => header.GetHash(),
|
||||
header => txsFetch.First(result => result.BlockId == header.GetHash()).Height!);
|
||||
|
||||
return new TxInfoResponse()
|
||||
return new TxInfoResponse
|
||||
{
|
||||
Txs = txsFetch.ToDictionary(tx => tx.TransactionHash.ToString(), tx => new TransactionResponse()
|
||||
Txs = txsFetch.ToDictionary(tx => tx.TransactionHash.ToString(), tx => new TransactionResponse
|
||||
{
|
||||
BlockHash = tx.BlockId?.ToString(),
|
||||
BlockHeight = (int?) tx.Height,
|
||||
|
@ -248,6 +222,7 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
BlockHeghts = headerToHeight.ToDictionary(kv => kv.Key.ToString(), kv =>(int) kv.Value!)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> DeriveScript(string identifier)
|
||||
{
|
||||
var cancellationToken = Context.ConnectionAborted;
|
||||
|
@ -353,7 +328,6 @@ var resultPsbt = PSBT.Parse(psbt, explorerClient.Network.NBitcoinNetwork);
|
|||
|
||||
public async Task<AppHandshakeResponse> Handshake(AppHandshake request)
|
||||
{
|
||||
|
||||
return await _appState.Handshake(Context.ConnectionId, request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,12 +88,12 @@ public class BTCPayAppState : IHostedService
|
|||
|
||||
private async Task StoreCreatedEvent(StoreCreatedEvent arg)
|
||||
{
|
||||
await _hubContext.Clients.Group(arg.Store.Id).NotifyServerEvent(new ServerEvent<StoreCreatedEvent>("store-created", arg));
|
||||
await _hubContext.Clients.Group(arg.StoreId).NotifyServerEvent(new ServerEvent<StoreCreatedEvent>("store-created", arg));
|
||||
}
|
||||
|
||||
private async Task StoreUpdatedEvent(StoreUpdatedEvent arg)
|
||||
{
|
||||
await _hubContext.Clients.Group(arg.Store.Id).NotifyServerEvent(new ServerEvent<StoreUpdatedEvent>("store-updated", arg));
|
||||
await _hubContext.Clients.Group(arg.StoreId).NotifyServerEvent(new ServerEvent<StoreUpdatedEvent>("store-updated", arg));
|
||||
}
|
||||
|
||||
private async Task StoreRemovedEvent(StoreRemovedEvent arg)
|
||||
|
@ -106,6 +106,7 @@ public class BTCPayAppState : IHostedService
|
|||
var ev = new ServerEvent<UserStoreAddedEvent>("user-store-added", arg);
|
||||
await _hubContext.Clients.Group(arg.StoreId).NotifyServerEvent(ev);
|
||||
await _hubContext.Clients.Group(arg.UserId).NotifyServerEvent(ev);
|
||||
//await _hubContext.Clients.User(arg.UserId) .AddToGroupAsync(Context.ConnectionId, arg.StoreId);
|
||||
}
|
||||
|
||||
private async Task StoreUserUpdatedEvent(UserStoreUpdatedEvent arg)
|
||||
|
@ -120,6 +121,7 @@ public class BTCPayAppState : IHostedService
|
|||
var ev = new ServerEvent<UserStoreRemovedEvent>("user-store-removed", arg);
|
||||
await _hubContext.Clients.Group(arg.StoreId).NotifyServerEvent(ev);
|
||||
await _hubContext.Clients.Group(arg.UserId).NotifyServerEvent(ev);
|
||||
// await Groups.RemoveFromGroupAsync(Context.ConnectionId, arg.UserId);
|
||||
}
|
||||
|
||||
private string _nodeInfo = string.Empty;
|
||||
|
@ -145,14 +147,11 @@ public class BTCPayAppState : IHostedService
|
|||
_nodeInfo = newInf.ToString();
|
||||
await _hubContext.Clients.All.NotifyServerNode(_nodeInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error during node info update");
|
||||
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromMinutes(string.IsNullOrEmpty(_nodeInfo)? 1:5), _cts.Token);
|
||||
}
|
||||
|
@ -167,10 +166,9 @@ public class BTCPayAppState : IHostedService
|
|||
var explorer = _explorerClientProvider.GetExplorerClient(obj.CryptoCode);
|
||||
var expandedTx = await explorer.GetTransactionAsync(obj.NewTransactionEvent.TrackedSource,
|
||||
obj.NewTransactionEvent.TransactionData.TransactionHash);
|
||||
|
||||
await _hubContext.Clients
|
||||
.Group(identifier)
|
||||
.TransactionDetected(new TransactionDetectedRequest()
|
||||
.TransactionDetected(new TransactionDetectedRequest
|
||||
{
|
||||
SpentScripts = expandedTx.Inputs.Select(input => input.ScriptPubKey.ToHex()).ToArray(),
|
||||
ReceivedScripts = expandedTx.Outputs.Select(output => output.ScriptPubKey.ToHex()).ToArray(),
|
||||
|
@ -258,7 +256,7 @@ public class BTCPayAppState : IHostedService
|
|||
}
|
||||
return false;
|
||||
}
|
||||
else if (GroupToConnectionId.TryGetValue(group, out var connId) && connId == contextConnectionId)
|
||||
if (GroupToConnectionId.TryGetValue(group, out var connId) && connId == contextConnectionId)
|
||||
{
|
||||
return GroupToConnectionId.TryRemove(group, out _);
|
||||
}
|
||||
|
@ -290,6 +288,4 @@ public class BTCPayAppState : IHostedService
|
|||
_logger.LogInformation($"Payment update for {identifier} {lightningPayment.Value} {lightningPayment.PaymentHash}");
|
||||
OnPaymentUpdate?.Invoke(this, (identifier, lightningPayment));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@ using BTCPayServer.Data;
|
|||
|
||||
namespace BTCPayServer.Events;
|
||||
|
||||
public class StoreCreatedEvent(StoreData store)
|
||||
public class StoreCreatedEvent(StoreData store) : StoreEvent(store)
|
||||
{
|
||||
public StoreData Store { get; } = store;
|
||||
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"Store \"{Store.StoreName}\" has been created";
|
||||
return $"{base.ToString()} has been created";
|
||||
}
|
||||
}
|
||||
|
|
13
BTCPayServer/Events/StoreEvent.cs
Normal file
13
BTCPayServer/Events/StoreEvent.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Events;
|
||||
|
||||
public class StoreEvent(StoreData store)
|
||||
{
|
||||
public string StoreId { get; } = store.Id;
|
||||
|
||||
protected new virtual string ToString()
|
||||
{
|
||||
return $"StoreEvent: Store \"{store.StoreName}\" ({store.Id})";
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Events;
|
||||
|
||||
public class StoreRemovedEvent(string storeId)
|
||||
public class StoreRemovedEvent(StoreData store) : StoreEvent(store)
|
||||
{
|
||||
public string StoreId { get; } = storeId;
|
||||
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"Store {StoreId} has been removed";
|
||||
return $"{base.ToString()} has been removed";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@ using BTCPayServer.Data;
|
|||
|
||||
namespace BTCPayServer.Events;
|
||||
|
||||
public class StoreUpdatedEvent(StoreData store)
|
||||
public class StoreUpdatedEvent(StoreData store) : StoreEvent(store)
|
||||
{
|
||||
public StoreData Store { get; } = store;
|
||||
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"Store \"{Store.StoreName}\" has been updated";
|
||||
return $"{base.ToString()} has been updated";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ namespace BTCPayServer.Events;
|
|||
|
||||
public class UserStoreAddedEvent(string storeId, string userId) : UserStoreEvent(storeId, userId)
|
||||
{
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"User {UserId} has been added to store {StoreId}";
|
||||
return $"{base.ToString()} has been added";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ public abstract class UserStoreEvent(string storeId, string userId)
|
|||
{
|
||||
public string StoreId { get; } = storeId;
|
||||
public string UserId { get; } = userId;
|
||||
public new virtual string ToString()
|
||||
|
||||
protected new virtual string ToString()
|
||||
{
|
||||
return $"StoreUserEvent: User {UserId}, Store {StoreId}";
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ namespace BTCPayServer.Events;
|
|||
|
||||
public class UserStoreRemovedEvent(string storeId, string userId) : UserStoreEvent(storeId, userId)
|
||||
{
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"User {UserId} has been removed from store {StoreId}";
|
||||
return $"{base.ToString()} has been removed";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ namespace BTCPayServer.Events;
|
|||
|
||||
public class UserStoreUpdatedEvent(string storeId, string userId) : UserStoreEvent(storeId, userId)
|
||||
{
|
||||
public override string ToString()
|
||||
protected override string ToString()
|
||||
{
|
||||
return $"User {UserId} and store {StoreId} relation has been updated";
|
||||
return $"{base.ToString()} has been updated";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -347,14 +347,14 @@ namespace BTCPayServer.Services.Stores
|
|||
public async Task CleanUnreachableStores()
|
||||
{
|
||||
await using var ctx = _ContextFactory.CreateContext();
|
||||
var events = new List<Events.StoreRemovedEvent>();
|
||||
var events = new List<StoreRemovedEvent>();
|
||||
foreach (var store in await ctx.Stores.Include(data => data.UserStores)
|
||||
.ThenInclude(store => store.StoreRole).Where(s =>
|
||||
s.UserStores.All(u => !u.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings)))
|
||||
.ToArrayAsync())
|
||||
{
|
||||
ctx.Stores.Remove(store);
|
||||
events.Add(new Events.StoreRemovedEvent(store.Id));
|
||||
events.Add(new StoreRemovedEvent(store));
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
events.ForEach(e => _eventAggregator.Publish(e));
|
||||
|
@ -385,7 +385,7 @@ namespace BTCPayServer.Services.Stores
|
|||
{
|
||||
ctx.Stores.Remove(store);
|
||||
await ctx.SaveChangesAsync();
|
||||
_eventAggregator.Publish(new StoreRemovedEvent(store.Id));
|
||||
_eventAggregator.Publish(new StoreRemovedEvent(store));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -583,7 +583,7 @@ retry:
|
|||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
_eventAggregator.Publish(new StoreRemovedEvent(store.Id));
|
||||
_eventAggregator.Publish(new StoreRemovedEvent(store));
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsDeadlock(ex) && retry < 5)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue