Show index of payment address for onchain payments

This commit is contained in:
Kukks 2020-08-09 16:00:58 +02:00
parent 2711f2cb2f
commit 8e8415515d
15 changed files with 92 additions and 97 deletions

View file

@ -600,7 +600,7 @@ namespace BTCPayServer.Controllers
try try
{ {
leases.Add(_EventAggregator.Subscribe<Events.InvoiceDataChangedEvent>(async o => await NotifySocket(webSocket, o.InvoiceId, invoiceId))); leases.Add(_EventAggregator.Subscribe<Events.InvoiceDataChangedEvent>(async o => await NotifySocket(webSocket, o.InvoiceId, invoiceId)));
leases.Add(_EventAggregator.Subscribe<Events.InvoiceNewAddressEvent>(async o => await NotifySocket(webSocket, o.InvoiceId, invoiceId))); leases.Add(_EventAggregator.Subscribe<Events.InvoiceNewPaymentDetailsEvent>(async o => await NotifySocket(webSocket, o.InvoiceId, invoiceId)));
leases.Add(_EventAggregator.Subscribe<Events.InvoiceEvent>(async o => await NotifySocket(webSocket, o.Invoice.Id, invoiceId))); leases.Add(_EventAggregator.Subscribe<Events.InvoiceEvent>(async o => await NotifySocket(webSocket, o.Invoice.Id, invoiceId)));
while (true) while (true)
{ {

View file

@ -1,20 +0,0 @@
namespace BTCPayServer.Events
{
public class InvoiceNewAddressEvent
{
public InvoiceNewAddressEvent(string invoiceId, string address, BTCPayNetworkBase network)
{
Address = address;
InvoiceId = invoiceId;
Network = network;
}
public string Address { get; set; }
public string InvoiceId { get; set; }
public BTCPayNetworkBase Network { get; set; }
public override string ToString()
{
return $"{Network.CryptoCode}: New address {Address} for invoice {InvoiceId}";
}
}
}

View file

@ -0,0 +1,24 @@
using BTCPayServer.Payments;
namespace BTCPayServer.Events
{
public class InvoiceNewPaymentDetailsEvent
{
public InvoiceNewPaymentDetailsEvent(string invoiceId, IPaymentMethodDetails details, PaymentMethodId paymentMethodId)
{
InvoiceId = invoiceId;
Details = details;
PaymentMethodId = paymentMethodId;
}
public string Address { get; set; }
public string InvoiceId { get; set; }
public IPaymentMethodDetails Details { get; }
public PaymentMethodId PaymentMethodId { get; }
public override string ToString()
{
return $"{PaymentMethodId.ToPrettyString()}: New payment details {Details.GetPaymentDestination()} for invoice {InvoiceId}";
}
}
}

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using NBitcoin; using NBitcoin;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -24,9 +25,10 @@ namespace BTCPayServer.Payments.Bitcoin
return FeeRate.SatoshiPerByte; return FeeRate.SatoshiPerByte;
} }
public void SetPaymentDestination(string newPaymentDestination) public void SetPaymentDetails(IPaymentMethodDetails newPaymentMethodDetails)
{ {
DepositAddress = newPaymentDestination; DepositAddress = newPaymentMethodDetails.GetPaymentDestination();
KeyPath = (newPaymentMethodDetails as BitcoinLikeOnChainPaymentMethod)?.KeyPath;
} }
public NetworkFeeMode NetworkFeeMode { get; set; } public NetworkFeeMode NetworkFeeMode { get; set; }
@ -51,7 +53,9 @@ namespace BTCPayServer.Payments.Bitcoin
[JsonIgnore] [JsonIgnore]
public Money NextNetworkFee { get; set; } public Money NextNetworkFee { get; set; }
[JsonIgnore] [JsonIgnore]
public String DepositAddress { get; set; } public String DepositAddress { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
public KeyPath KeyPath { get; set; }
public BitcoinAddress GetDepositAddress(Network network) public BitcoinAddress GetDepositAddress(Network network)
{ {

View file

@ -17,13 +17,14 @@ namespace BTCPayServer.Payments.Bitcoin
} }
public BitcoinLikePaymentData(BitcoinAddress address, IMoney value, OutPoint outpoint, bool rbf) public BitcoinLikePaymentData(BitcoinAddress address, IMoney value, OutPoint outpoint, bool rbf, KeyPath keyPath)
{ {
Address = address; Address = address;
Value = value; Value = value;
Outpoint = outpoint; Outpoint = outpoint;
ConfirmationCount = 0; ConfirmationCount = 0;
RBF = rbf; RBF = rbf;
KeyPath = keyPath;
} }
[JsonIgnore] [JsonIgnore]
public BTCPayNetworkBase Network { get; set; } public BTCPayNetworkBase Network { get; set; }
@ -34,6 +35,8 @@ namespace BTCPayServer.Payments.Bitcoin
public int ConfirmationCount { get; set; } public int ConfirmationCount { get; set; }
public bool RBF { get; set; } public bool RBF { get; set; }
public BitcoinAddress Address { get; set; } public BitcoinAddress Address { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
public KeyPath KeyPath { get; set; }
public IMoney Value { get; set; } public IMoney Value { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]

View file

@ -159,7 +159,9 @@ namespace BTCPayServer.Payments.Bitcoin
break; break;
} }
onchainMethod.DepositAddress = (await prepare.ReserveAddress).Address.ToString(); var reserved = await prepare.ReserveAddress;
onchainMethod.DepositAddress = reserved.Address.ToString();
onchainMethod.KeyPath = reserved.KeyPath;
onchainMethod.PayjoinEnabled = blob.PayJoinEnabled && onchainMethod.PayjoinEnabled = blob.PayJoinEnabled &&
PayjoinClient.SupportedFormats.Contains(supportedPaymentMethod PayjoinClient.SupportedFormats.Contains(supportedPaymentMethod
.AccountDerivation.ScriptPubKeyType()) && .AccountDerivation.ScriptPubKeyType()) &&

View file

@ -157,7 +157,7 @@ namespace BTCPayServer.Payments.Bitcoin
var paymentData = new BitcoinLikePaymentData(address, var paymentData = new BitcoinLikePaymentData(address,
output.matchedOutput.Value, output.outPoint, output.matchedOutput.Value, output.outPoint,
evt.TransactionData.Transaction.RBF); evt.TransactionData.Transaction.RBF, output.Item1.KeyPath);
var alreadyExist = invoice.GetAllBitcoinPaymentData().Where(c => c.GetPaymentId() == paymentData.GetPaymentId()).Any(); var alreadyExist = invoice.GetAllBitcoinPaymentData().Where(c => c.GetPaymentId() == paymentData.GetPaymentId()).Any();
if (!alreadyExist) if (!alreadyExist)
@ -363,7 +363,7 @@ namespace BTCPayServer.Payments.Bitcoin
var address = network.NBXplorerNetwork.CreateAddress(strategy, coin.KeyPath, coin.ScriptPubKey); var address = network.NBXplorerNetwork.CreateAddress(strategy, coin.KeyPath, coin.ScriptPubKey);
var paymentData = new BitcoinLikePaymentData(address, coin.Value, coin.OutPoint, var paymentData = new BitcoinLikePaymentData(address, coin.Value, coin.OutPoint,
transaction.Transaction.RBF); transaction.Transaction.RBF, coin.KeyPath);
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.Timestamp, paymentData, network).ConfigureAwait(false); var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.Timestamp, paymentData, network).ConfigureAwait(false);
alreadyAccounted.Add(coin.OutPoint); alreadyAccounted.Add(coin.OutPoint);
@ -400,8 +400,9 @@ namespace BTCPayServer.Payments.Bitcoin
{ {
var address = await wallet.ReserveAddressAsync(strategy); var address = await wallet.ReserveAddressAsync(strategy);
btc.DepositAddress = address.Address.ToString(); btc.DepositAddress = address.Address.ToString();
await _InvoiceRepository.NewAddress(invoice.Id, btc, wallet.Network); btc.KeyPath = address.KeyPath;
_Aggregator.Publish(new InvoiceNewAddressEvent(invoice.Id, btc.DepositAddress, wallet.Network)); await _InvoiceRepository.NewPaymentDetails(invoice.Id, btc, wallet.Network);
_Aggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoice.Id, btc, paymentMethod.GetId()));
paymentMethod.SetPaymentMethodDetails(btc); paymentMethod.SetPaymentMethodDetails(btc);
invoice.SetPaymentMethod(paymentMethod); invoice.SetPaymentMethod(paymentMethod);
} }

View file

@ -21,10 +21,5 @@ namespace BTCPayServer.Payments
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
decimal GetFeeRate(); decimal GetFeeRate();
/// <summary>
/// Change the payment destination (internal plumbing)
/// </summary>
/// <param name="newPaymentDestination"></param>
void SetPaymentDestination(string newPaymentDestination);
} }
} }

View file

@ -26,9 +26,9 @@ namespace BTCPayServer.Payments.Lightning
return 0.0m; return 0.0m;
} }
public void SetPaymentDestination(string newPaymentDestination) public void SetPaymentDetails(IPaymentMethodDetails newPaymentMethodDetails)
{ {
BOLT11 = newPaymentDestination; BOLT11 = newPaymentMethodDetails.GetPaymentDestination();
} }
} }
} }

View file

@ -238,6 +238,7 @@ namespace BTCPayServer.Payments.PayJoin
Dictionary<OutPoint, UTXO> selectedUTXOs = new Dictionary<OutPoint, UTXO>(); Dictionary<OutPoint, UTXO> selectedUTXOs = new Dictionary<OutPoint, UTXO>();
PSBTOutput originalPaymentOutput = null; PSBTOutput originalPaymentOutput = null;
BitcoinAddress paymentAddress = null; BitcoinAddress paymentAddress = null;
KeyPath paymentAddressIndex = null;
InvoiceEntity invoice = null; InvoiceEntity invoice = null;
DerivationSchemeSettings derivationSchemeSettings = null; DerivationSchemeSettings derivationSchemeSettings = null;
foreach (var output in psbt.Outputs) foreach (var output in psbt.Outputs)
@ -300,6 +301,7 @@ namespace BTCPayServer.Payments.PayJoin
ctx.LockedUTXOs = selectedUTXOs.Select(u => u.Key).ToArray(); ctx.LockedUTXOs = selectedUTXOs.Select(u => u.Key).ToArray();
originalPaymentOutput = output; originalPaymentOutput = output;
paymentAddress = paymentDetails.GetDepositAddress(network.NBitcoinNetwork); paymentAddress = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
paymentAddressIndex = paymentDetails.KeyPath;
break; break;
} }
@ -440,7 +442,7 @@ namespace BTCPayServer.Payments.PayJoin
var originalPaymentData = new BitcoinLikePaymentData(paymentAddress, var originalPaymentData = new BitcoinLikePaymentData(paymentAddress,
originalPaymentOutput.Value, originalPaymentOutput.Value,
new OutPoint(ctx.OriginalTransaction.GetHash(), originalPaymentOutput.Index), new OutPoint(ctx.OriginalTransaction.GetHash(), originalPaymentOutput.Index),
ctx.OriginalTransaction.RBF); ctx.OriginalTransaction.RBF, paymentAddressIndex);
originalPaymentData.ConfirmationCount = -1; originalPaymentData.ConfirmationCount = -1;
originalPaymentData.PayjoinInformation = new PayjoinInformation() originalPaymentData.PayjoinInformation = new PayjoinInformation()
{ {

View file

@ -25,6 +25,11 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Payments
return 0; return 0;
} }
public void SetPaymentDetails(IPaymentMethodDetails newPaymentMethodDetails)
{
throw new System.NotImplementedException();
}
public void SetPaymentDestination(string newPaymentDestination) public void SetPaymentDestination(string newPaymentDestination)
{ {
DepositAddress = newPaymentDestination; DepositAddress = newPaymentDestination;

View file

@ -25,10 +25,6 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
return 0.0m; return 0.0m;
} }
public void SetPaymentDestination(string newPaymentDestination)
{
DepositAddress = newPaymentDestination;
}
public long AccountIndex { get; set; } public long AccountIndex { get; set; }
public long AddressIndex { get; set; } public long AddressIndex { get; set; }
public string DepositAddress { get; set; } public string DepositAddress { get; set; }

View file

@ -135,9 +135,9 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
}); });
monero.DepositAddress = address.Address; monero.DepositAddress = address.Address;
monero.AddressIndex = address.AddressIndex; monero.AddressIndex = address.AddressIndex;
await _invoiceRepository.NewAddress(invoice.Id, monero, payment.Network); await _invoiceRepository.NewPaymentDetails(invoice.Id, monero, payment.Network);
_eventAggregator.Publish( _eventAggregator.Publish(
new InvoiceNewAddressEvent(invoice.Id, address.Address, payment.Network)); new InvoiceNewPaymentDetailsEvent(invoice.Id, monero, payment.GetPaymentMethodId()));
paymentMethod.SetPaymentMethodDetails(monero); paymentMethod.SetPaymentMethodDetails(monero);
invoice.SetPaymentMethod(paymentMethod); invoice.SetPaymentMethod(paymentMethod);
} }

View file

@ -245,67 +245,48 @@ retry:
return paymentMethod.GetPaymentMethodDetails().GetPaymentDestination(); return paymentMethod.GetPaymentMethodDetails().GetPaymentDestination();
} }
public async Task<bool> NewAddress(string invoiceId, IPaymentMethodDetails paymentMethod, BTCPayNetworkBase network) public async Task<bool> NewPaymentDetails(string invoiceId, IPaymentMethodDetails paymentMethodDetails, BTCPayNetworkBase network)
{ {
using (var context = _ContextFactory.CreateContext()) await using var context = _ContextFactory.CreateContext();
var invoice = (await context.Invoices.Where(i => i.Id == invoiceId).ToListAsync()).FirstOrDefault();
if (invoice == null)
return false;
var invoiceEntity = invoice.GetBlob(_Networks);
var paymentMethod = invoiceEntity.GetPaymentMethod(network, paymentMethodDetails.GetPaymentType());
if (paymentMethod == null)
return false;
var existingPaymentMethod = paymentMethod.GetPaymentMethodDetails();
if (existingPaymentMethod.GetPaymentDestination() != null)
{ {
var invoice = (await context.Invoices.Where(i => i.Id == invoiceId).ToListAsync()).FirstOrDefault(); MarkUnassigned(invoiceId, invoiceEntity, context, paymentMethod.GetId());
if (invoice == null) }
return false; paymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
var invoiceEntity = invoice.GetBlob(_Networks);
var currencyData = invoiceEntity.GetPaymentMethod(network, paymentMethod.GetPaymentType());
if (currencyData == null)
return false;
var existingPaymentMethod = currencyData.GetPaymentMethodDetails();
if (existingPaymentMethod.GetPaymentDestination() != null)
{
MarkUnassigned(invoiceId, invoiceEntity, context, currencyData.GetId());
}
existingPaymentMethod.SetPaymentDestination(paymentMethod.GetPaymentDestination());
currencyData.SetPaymentMethodDetails(existingPaymentMethod);
#pragma warning disable CS0618 #pragma warning disable CS0618
if (network.IsBTC) if (network.IsBTC)
{ {
invoiceEntity.DepositAddress = currencyData.DepositAddress; invoiceEntity.DepositAddress = paymentMethod.DepositAddress;
} }
#pragma warning restore CS0618 #pragma warning restore CS0618
invoiceEntity.SetPaymentMethod(currencyData); invoiceEntity.SetPaymentMethod(paymentMethod);
invoice.Blob = ToBytes(invoiceEntity, network); invoice.Blob = ToBytes(invoiceEntity, network);
context.AddressInvoices.Add(new AddressInvoiceData() await context.AddressInvoices.AddAsync(new AddressInvoiceData()
{ {
InvoiceDataId = invoiceId, InvoiceDataId = invoiceId,
CreatedTime = DateTimeOffset.UtcNow CreatedTime = DateTimeOffset.UtcNow
} }
.Set(GetDestination(currencyData), currencyData.GetId())); .Set(GetDestination(paymentMethod), paymentMethod.GetId()));
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData() await context.HistoricalAddressInvoices.AddAsync(new HistoricalAddressInvoiceData()
{
InvoiceDataId = invoiceId,
Assigned = DateTimeOffset.UtcNow
}.SetAddress(paymentMethod.GetPaymentDestination(), network.CryptoCode));
await context.SaveChangesAsync();
AddToTextSearch(invoice.Id, paymentMethod.GetPaymentDestination());
return true;
}
}
public async Task UpdateInvoicePaymentMethod(string invoiceId, PaymentMethod paymentMethod)
{
using (var context = _ContextFactory.CreateContext())
{ {
var invoice = await context.Invoices.FindAsync(invoiceId); InvoiceDataId = invoiceId,
if (invoice == null) Assigned = DateTimeOffset.UtcNow
return; }.SetAddress(paymentMethodDetails.GetPaymentDestination(), network.CryptoCode));
var network = paymentMethod.Network;
var invoiceEntity = invoice.GetBlob(_Networks); await context.SaveChangesAsync();
invoiceEntity.SetPaymentMethod(paymentMethod); AddToTextSearch(invoice.Id, paymentMethodDetails.GetPaymentDestination());
invoice.Blob = ToBytes(invoiceEntity, network); return true;
await context.SaveChangesAsync();
}
} }
public async Task AddPendingInvoiceIfNotPresent(string invoiceId) public async Task AddPendingInvoiceIfNotPresent(string invoiceId)

View file

@ -54,6 +54,7 @@
<thead class="thead-inverse"> <thead class="thead-inverse">
<tr> <tr>
<th>Crypto</th> <th>Crypto</th>
<th>Index</th>
<th>Deposit address</th> <th>Deposit address</th>
<th>Amount</th> <th>Amount</th>
<th>Transaction Id</th> <th>Transaction Id</th>
@ -65,9 +66,10 @@
{ {
<tr style="@(payment.Replaced ? "text-decoration: line-through" : "")"> <tr style="@(payment.Replaced ? "text-decoration: line-through" : "")">
<td>@payment.Crypto</td> <td>@payment.Crypto</td>
<td>@payment.DepositAddress</td> <td>@(payment.CryptoPaymentData.Index?.ToString()?? "Unknown")</td>
<td style="max-width:300px;" data-toggle="tooltip" class="text-truncate" title="@payment.DepositAddress">@payment.DepositAddress</td>
<td class="payment-value">@payment.CryptoPaymentData.GetValue() @Safe.Raw(payment.AdditionalInformation is string i ? $"<br/>({i})" : string.Empty)</td> <td class="payment-value">@payment.CryptoPaymentData.GetValue() @Safe.Raw(payment.AdditionalInformation is string i ? $"<br/>({i})" : string.Empty)</td>
<td> <td style="max-width:300px;" data-toggle="tooltip" class="text-truncate" title="@payment.TransactionId">
<div class="wraptextAuto"> <div class="wraptextAuto">
<a href="@payment.TransactionLink" target="_blank"> <a href="@payment.TransactionLink" target="_blank">
@payment.TransactionId @payment.TransactionId