2022-10-11 10:34:29 +02:00
|
|
|
#nullable enable
|
2020-04-28 08:06:28 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using BTCPayServer.Data;
|
|
|
|
using BTCPayServer.Events;
|
2021-11-22 09:16:08 +01:00
|
|
|
using BTCPayServer.Logging;
|
2020-04-28 08:06:28 +02:00
|
|
|
using BTCPayServer.Payments;
|
|
|
|
using BTCPayServer.Payments.Bitcoin;
|
|
|
|
using BTCPayServer.Services;
|
2020-09-17 16:39:55 +02:00
|
|
|
using BTCPayServer.Services.Apps;
|
|
|
|
using BTCPayServer.Services.PaymentRequests;
|
2020-04-28 08:06:28 +02:00
|
|
|
using NBitcoin;
|
2022-12-13 01:09:25 +01:00
|
|
|
using NBXplorer.DerivationStrategy;
|
2020-04-28 08:06:28 +02:00
|
|
|
|
|
|
|
namespace BTCPayServer.HostedServices
|
|
|
|
{
|
|
|
|
public class TransactionLabelMarkerHostedService : EventHostedServiceBase
|
|
|
|
{
|
|
|
|
private readonly WalletRepository _walletRepository;
|
|
|
|
|
2022-12-13 01:09:25 +01:00
|
|
|
public BTCPayNetworkProvider NetworkProvider { get; }
|
|
|
|
|
|
|
|
public TransactionLabelMarkerHostedService(BTCPayNetworkProvider networkProvider, EventAggregator eventAggregator, WalletRepository walletRepository, Logs logs) :
|
2021-11-22 09:16:08 +01:00
|
|
|
base(eventAggregator, logs)
|
2020-04-28 08:06:28 +02:00
|
|
|
{
|
2022-12-13 01:09:25 +01:00
|
|
|
NetworkProvider = networkProvider;
|
2020-04-28 08:06:28 +02:00
|
|
|
_walletRepository = walletRepository;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void SubscribeToEvents()
|
|
|
|
{
|
|
|
|
Subscribe<InvoiceEvent>();
|
2022-12-01 01:54:55 +01:00
|
|
|
Subscribe<NewOnChainTransactionEvent>();
|
2020-04-28 08:06:28 +02:00
|
|
|
}
|
|
|
|
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
|
|
|
|
{
|
2022-12-01 01:54:55 +01:00
|
|
|
switch (evt)
|
2020-04-28 08:06:28 +02:00
|
|
|
{
|
2022-12-01 01:54:55 +01:00
|
|
|
// For each new transaction that we detect, we check if we can find
|
|
|
|
// any utxo or script object matching it.
|
|
|
|
// If we find, then we create a link between them and the tx object.
|
|
|
|
case NewOnChainTransactionEvent transactionEvent:
|
2022-12-13 01:09:25 +01:00
|
|
|
{
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(transactionEvent.CryptoCode);
|
|
|
|
var derivation = transactionEvent.NewTransactionEvent.DerivationStrategy;
|
|
|
|
if (network is null || derivation is null)
|
|
|
|
break;
|
|
|
|
var txHash = transactionEvent.NewTransactionEvent.TransactionData.TransactionHash.ToString();
|
2022-12-01 01:54:55 +01:00
|
|
|
|
2022-12-13 01:09:25 +01:00
|
|
|
// find all wallet objects that fit this transaction
|
|
|
|
// that means see if there are any utxo objects that match in/outs and scripts/addresses that match outs
|
|
|
|
var matchedObjects = transactionEvent.NewTransactionEvent.TransactionData.Transaction.Inputs
|
|
|
|
.Select<TxIn, ObjectTypeId>(txIn => new ObjectTypeId(WalletObjectData.Types.Utxo, txIn.PrevOut.ToString()))
|
|
|
|
.Concat(transactionEvent.NewTransactionEvent.Outputs.SelectMany<NBXplorer.Models.MatchedOutput, ObjectTypeId>(txOut =>
|
2022-12-01 01:54:55 +01:00
|
|
|
|
2022-12-13 01:09:25 +01:00
|
|
|
new[]{
|
|
|
|
new ObjectTypeId(WalletObjectData.Types.Address, GetAddress(derivation, txOut, network).ToString()),
|
|
|
|
new ObjectTypeId(WalletObjectData.Types.Utxo, new OutPoint(transactionEvent.NewTransactionEvent.TransactionData.TransactionHash, (uint)txOut.Index).ToString())
|
|
|
|
|
|
|
|
})).Distinct().ToArray();
|
|
|
|
|
|
|
|
var objs = await _walletRepository.GetWalletObjects(new GetWalletObjectsQuery() { TypesIds = matchedObjects });
|
2023-11-28 11:38:09 +01:00
|
|
|
var links = new List<WalletObjectLinkData>();
|
2022-12-13 01:09:25 +01:00
|
|
|
foreach (var walletObjectDatas in objs.GroupBy(data => data.Key.WalletId))
|
2022-12-01 01:54:55 +01:00
|
|
|
{
|
2022-12-13 01:09:25 +01:00
|
|
|
var txWalletObject = new WalletObjectId(walletObjectDatas.Key,
|
|
|
|
WalletObjectData.Types.Tx, txHash);
|
|
|
|
foreach (var walletObjectData in walletObjectDatas)
|
|
|
|
{
|
2023-11-28 11:38:09 +01:00
|
|
|
links.Add(
|
|
|
|
WalletRepository.NewWalletObjectLinkData(txWalletObject, walletObjectData.Key));
|
2023-02-22 03:47:02 +01:00
|
|
|
//if the object is an address, we also link the labels to the tx
|
2023-04-10 04:07:03 +02:00
|
|
|
if (walletObjectData.Value.Type == WalletObjectData.Types.Address)
|
2023-02-22 03:47:02 +01:00
|
|
|
{
|
2023-04-07 08:58:41 +02:00
|
|
|
var neighbours = walletObjectData.Value.GetNeighbours().ToArray();
|
|
|
|
var labels = neighbours
|
2023-02-22 03:47:02 +01:00
|
|
|
.Where(data => data.Type == WalletObjectData.Types.Label).Select(data =>
|
|
|
|
new WalletObjectId(walletObjectDatas.Key, data.Type, data.Id));
|
2023-04-10 04:07:03 +02:00
|
|
|
foreach (var label in labels)
|
|
|
|
{
|
2023-11-28 11:38:09 +01:00
|
|
|
links.Add(WalletRepository.NewWalletObjectLinkData(label, txWalletObject));
|
2023-04-10 04:07:03 +02:00
|
|
|
var attachments = neighbours.Where(data => data.Type == label.Id);
|
|
|
|
foreach (var attachment in attachments)
|
|
|
|
{
|
2023-11-28 11:38:09 +01:00
|
|
|
links.Add(WalletRepository.NewWalletObjectLinkData(new WalletObjectId(walletObjectDatas.Key, attachment.Type, attachment.Id), txWalletObject));
|
2023-04-10 04:07:03 +02:00
|
|
|
}
|
|
|
|
}
|
2023-02-22 03:47:02 +01:00
|
|
|
}
|
2022-12-13 01:09:25 +01:00
|
|
|
}
|
2022-12-01 01:54:55 +01:00
|
|
|
}
|
2023-11-28 11:38:09 +01:00
|
|
|
await _walletRepository.EnsureCreated(null,links);
|
2022-12-01 01:54:55 +01:00
|
|
|
|
2022-12-13 01:09:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2023-01-06 14:18:07 +01:00
|
|
|
case InvoiceEvent { Name: InvoiceEvent.ReceivedPayment } invoiceEvent when
|
2022-12-01 01:54:55 +01:00
|
|
|
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance &&
|
|
|
|
invoiceEvent.Payment.GetCryptoPaymentData() is BitcoinLikePaymentData bitcoinLikePaymentData:
|
2023-01-06 14:18:07 +01:00
|
|
|
{
|
2023-07-19 11:47:32 +02:00
|
|
|
var walletId = new WalletId(invoiceEvent.Invoice.StoreId, invoiceEvent.Payment.Currency);
|
2023-01-06 14:18:07 +01:00
|
|
|
var transactionId = bitcoinLikePaymentData.Outpoint.Hash;
|
|
|
|
var labels = new List<Attachment>
|
2022-12-01 01:54:55 +01:00
|
|
|
{
|
|
|
|
Attachment.Invoice(invoiceEvent.Invoice.Id)
|
|
|
|
};
|
2023-04-07 08:58:41 +02:00
|
|
|
labels.AddRange(PaymentRequestRepository.GetPaymentIdsFromInternalTags(invoiceEvent.Invoice).Select(Attachment.PaymentRequest));
|
|
|
|
labels.AddRange(AppService.GetAppInternalTags(invoiceEvent.Invoice).Select(Attachment.App));
|
2020-09-17 16:39:55 +02:00
|
|
|
|
2023-01-06 14:18:07 +01:00
|
|
|
await _walletRepository.AddWalletTransactionAttachment(walletId, transactionId, labels);
|
|
|
|
break;
|
|
|
|
}
|
2020-04-28 08:06:28 +02:00
|
|
|
}
|
|
|
|
}
|
2022-12-13 01:09:25 +01:00
|
|
|
|
|
|
|
private BitcoinAddress GetAddress(DerivationStrategyBase derivationStrategy, NBXplorer.Models.MatchedOutput txOut, BTCPayNetwork network)
|
|
|
|
{
|
|
|
|
// Old version of NBX doesn't give address in the event, so we need to guess
|
|
|
|
return (txOut.Address ?? network.NBXplorerNetwork.CreateAddress(derivationStrategy, txOut.KeyPath, txOut.ScriptPubKey));
|
|
|
|
}
|
2020-04-28 08:06:28 +02:00
|
|
|
}
|
|
|
|
}
|