diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs
index 86c274be1..b92f4df27 100644
--- a/BTCPayServer.Tests/UnitTest1.cs
+++ b/BTCPayServer.Tests/UnitTest1.cs
@@ -519,6 +519,7 @@ namespace BTCPayServer.Tests
}, Facade.Merchant);
var cashCow = tester.ExplorerNode;
+ cashCow.Generate(2); // get some money in case
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
var firstPayment = Money.Coins(0.04m);
cashCow.SendToAddress(invoiceAddress, firstPayment);
@@ -553,6 +554,7 @@ namespace BTCPayServer.Tests
invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
firstPayment = Money.Coins(0.04m);
cashCow.SendToAddress(invoiceAddress, firstPayment);
+ Logs.Tester.LogInformation("First payment sent to " + invoiceAddress);
Eventually(() =>
{
invoice = user.BitPay.GetInvoice(invoice.Id);
@@ -566,7 +568,7 @@ namespace BTCPayServer.Tests
var secondPayment = Money.Coins(decimal.Parse(ltcCryptoInfo.Due));
cashCow.Generate(2); // LTC is not worth a lot, so just to make sure we have money...
cashCow.SendToAddress(invoiceAddress, secondPayment);
-
+ Logs.Tester.LogInformation("Second payment sent to " + invoiceAddress);
Eventually(() =>
{
invoice = user.BitPay.GetInvoice(invoice.Id);
@@ -765,7 +767,7 @@ namespace BTCPayServer.Tests
private void Eventually(Action act)
{
- CancellationTokenSource cts = new CancellationTokenSource(10000);
+ CancellationTokenSource cts = new CancellationTokenSource(20000);
while (true)
{
try
diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj
index 794789a78..968eb9bdf 100644
--- a/BTCPayServer/BTCPayServer.csproj
+++ b/BTCPayServer/BTCPayServer.csproj
@@ -2,7 +2,7 @@
Exe
netcoreapp2.0
- 1.0.1.16
+ 1.0.1.17
diff --git a/BTCPayServer/HostedServices/InvoiceWatcher.cs b/BTCPayServer/HostedServices/InvoiceWatcher.cs
index b813726b2..777fe6e97 100644
--- a/BTCPayServer/HostedServices/InvoiceWatcher.cs
+++ b/BTCPayServer/HostedServices/InvoiceWatcher.cs
@@ -68,7 +68,11 @@ namespace BTCPayServer.HostedServices
{
var invoice = await _InvoiceRepository.GetInvoiceIdFromScriptPubKey(scriptPubKey, network.CryptoCode);
if (invoice != null)
+ {
+ String address = scriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? scriptPubKey.ToString();
+ Logs.PayServer.LogInformation($"{address} is mapping to invoice {invoice}");
_WatchRequests.Add(invoice);
+ }
}
async Task NotifyBlock()
@@ -82,8 +86,11 @@ namespace BTCPayServer.HostedServices
private async Task UpdateInvoice(string invoiceId, CancellationToken cancellation)
{
Dictionary changes = new Dictionary();
- while (!cancellation.IsCancellationRequested)
+ int maxLoop = 5;
+ int loopCount = -1;
+ while (!cancellation.IsCancellationRequested && loopCount < maxLoop)
{
+ loopCount++;
try
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId, true).ConfigureAwait(false);
@@ -122,7 +129,7 @@ namespace BTCPayServer.HostedServices
break;
}
- if (!changed || cancellation.IsCancellationRequested)
+ if (updateContext.Events.Count == 0 || cancellation.IsCancellationRequested)
break;
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
@@ -483,29 +490,31 @@ namespace BTCPayServer.HostedServices
{
Logs.PayServer.LogInformation("Start watching invoices");
await Task.Delay(1).ConfigureAwait(false); // Small hack so that the caller does not block on GetConsumingEnumerable
- ConcurrentDictionary executing = new ConcurrentDictionary();
+ ConcurrentDictionary> executing = new ConcurrentDictionary>();
try
{
+ // This loop just make sure an invoice will not be updated at the same time by two tasks.
+ // If an update is happening while a request come, then the update is deferred when the executing task is over
foreach (var item in _WatchRequests.GetConsumingEnumerable(cancellation))
{
- bool added = false;
- var task = executing.GetOrAdd(item, async i =>
+ var localItem = item;
+ var toExecute = new Lazy(async () =>
{
try
{
- added = true;
- await UpdateInvoice(i, cancellation);
+ await UpdateInvoice(localItem, cancellation);
}
- catch (Exception ex) when (!cancellation.IsCancellationRequested)
+ finally
{
- Logs.PayServer.LogCritical(ex, $"Error in the InvoiceWatcher loop (Invoice {i})");
+ executing.TryRemove(localItem, out Lazy unused);
}
- });
-
- if (!added && task.Status == TaskStatus.RanToCompletion)
+ }, false);
+ var executingTask = executing.GetOrAdd(item, toExecute);
+ executingTask.Value.GetAwaiter(); // Make sure it run
+ if (executingTask != toExecute)
{
- executing.TryRemove(item, out Task t);
- _WatchRequests.Add(item);
+ // What was planned can't run for now, rebook it when the executingTask finish
+ var unused = executingTask.Value.ContinueWith(t => _WatchRequests.Add(localItem));
}
}
}
@@ -514,7 +523,7 @@ namespace BTCPayServer.HostedServices
}
finally
{
- await Task.WhenAll(executing.Values);
+ await Task.WhenAll(executing.Values.Select(v => v.Value).ToArray());
}
Logs.PayServer.LogInformation("Stop watching invoices");
}
diff --git a/BTCPayServer/wwwroot/js/core.js b/BTCPayServer/wwwroot/js/core.js
index 5de0cf34e..92508c9f8 100644
--- a/BTCPayServer/wwwroot/js/core.js
+++ b/BTCPayServer/wwwroot/js/core.js
@@ -19,7 +19,7 @@ Vue.config.ignoredElements = [
'low-fee-timeline',
// Ignoring custom HTML5 elements, eg: bp-spinner
/^bp-/
-]
+];
var checkoutCtrl = new Vue({
el: '#checkoutCtrl',
components: {
@@ -28,7 +28,7 @@ var checkoutCtrl = new Vue({
data: {
srvModel: srvModel
}
-})
+});
var display = $(".timer-row__time-left"); // Timer container
@@ -88,7 +88,7 @@ function emailForm() {
$("#emailAddressForm").addClass("ng-touched ng-dirty ng-submitted ng-invalid");
}
- })
+ });
}
/* =============== Even listeners =============== */
@@ -136,7 +136,7 @@ $("#copy-tab").click(function () {
$(".payment-tabs__slider").addClass("slide-right");
}
- if (!($("#copy").is(".active"))) {
+ if (!$("#copy").is(".active")) {
$("#copy").show();
$("#copy").addClass("active");
@@ -284,7 +284,7 @@ function progressStart(timerMax) {
var now = new Date();
var timeDiff = end.getTime() - now.getTime();
- var perc = 100 - Math.round((timeDiff / timerMax) * 100);
+ var perc = 100 - Math.round(timeDiff / timerMax * 100);
if (perc === 75 && (status === "paidPartial" || status === "new")) {
$(".timer-row").addClass("expiring-soon");