From 01d898b6180eaac658d4c14db38c974fc4ecafb1 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Thu, 15 Feb 2018 13:02:12 +0900 Subject: [PATCH] Caching GetCoins --- BTCPayServer/BTCPayServer.csproj | 2 +- BTCPayServer/Events/TxOutReceivedEvent.cs | 2 ++ BTCPayServer/HostedServices/InvoiceWatcher.cs | 30 ++++++---------- .../HostedServices/NBXplorerListener.cs | 3 +- BTCPayServer/Services/Wallets/BTCPayWallet.cs | 35 +++++++++++++------ .../Services/Wallets/BTCPayWalletProvider.cs | 6 +++- 6 files changed, 45 insertions(+), 33 deletions(-) diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index 1df38b930..c392836c0 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.0 - 1.0.1.26 + 1.0.1.27 NU1701 diff --git a/BTCPayServer/Events/TxOutReceivedEvent.cs b/BTCPayServer/Events/TxOutReceivedEvent.cs index d1b748a48..18cfeebee 100644 --- a/BTCPayServer/Events/TxOutReceivedEvent.cs +++ b/BTCPayServer/Events/TxOutReceivedEvent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NBitcoin; +using NBXplorer.DerivationStrategy; namespace BTCPayServer.Events { @@ -10,6 +11,7 @@ namespace BTCPayServer.Events { public BTCPayNetwork Network { get; set; } public Script ScriptPubKey { get; set; } + public DerivationStrategyBase DerivationStrategy { get; set; } public override string ToString() { diff --git a/BTCPayServer/HostedServices/InvoiceWatcher.cs b/BTCPayServer/HostedServices/InvoiceWatcher.cs index f47d18b20..d4f1aa6bb 100644 --- a/BTCPayServer/HostedServices/InvoiceWatcher.cs +++ b/BTCPayServer/HostedServices/InvoiceWatcher.cs @@ -29,9 +29,6 @@ namespace BTCPayServer.HostedServices { } - - public Dictionary KnownStates { get; set; } - public Dictionary ModifiedKnownStates { get; set; } = new Dictionary(); public InvoiceEntity Invoice { get; set; } public List Events { get; set; } = new List(); @@ -64,14 +61,15 @@ namespace BTCPayServer.HostedServices } CompositeDisposable leases = new CompositeDisposable(); - async Task NotifyReceived(Script scriptPubKey, BTCPayNetwork network) + async Task NotifyReceived(Events.TxOutReceivedEvent evt) { - var invoice = await _InvoiceRepository.GetInvoiceIdFromScriptPubKey(scriptPubKey, network.CryptoCode); - if (invoice != null) + var invoiceId = await _InvoiceRepository.GetInvoiceIdFromScriptPubKey(evt.ScriptPubKey, evt.Network.CryptoCode); + if (invoiceId != null) { - String address = scriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? scriptPubKey.ToString(); - Logs.PayServer.LogInformation($"{address} is mapping to invoice {invoice}"); - _WatchRequests.Add(invoice); + String address = evt.ScriptPubKey.GetDestinationAddress(evt.Network.NBitcoinNetwork)?.ToString() ?? evt.ScriptPubKey.ToString(); + _WalletProvider.GetWallet(evt.Network).InvalidateCache(evt.DerivationStrategy); + Logs.PayServer.LogInformation($"{address} is mapping to invoice {invoiceId}"); + _WatchRequests.Add(invoiceId); } } @@ -99,8 +97,7 @@ namespace BTCPayServer.HostedServices var stateBefore = invoice.Status; var updateContext = new UpdateInvoiceContext() { - Invoice = invoice, - KnownStates = changes + Invoice = invoice }; await UpdateInvoice(updateContext).ConfigureAwait(false); if (updateContext.Dirty) @@ -116,11 +113,6 @@ namespace BTCPayServer.HostedServices _EventAggregator.Publish(evt, evt.GetType()); } - foreach (var modifiedKnownState in updateContext.ModifiedKnownStates) - { - changes.AddOrReplace(modifiedKnownState.Key, modifiedKnownState.Value); - } - if (invoice.Status == "complete" || ((invoice.Status == "invalid" || invoice.Status == "expired") && invoice.MonitoringExpiration < DateTimeOffset.UtcNow)) { @@ -165,8 +157,6 @@ namespace BTCPayServer.HostedServices if (coins.TimestampedCoins.Length == 0) continue; bool dirtyAddress = false; - if (coins.State != null) - context.ModifiedKnownStates.AddOrReplace(coins.Wallet.Network, coins.State); var alreadyAccounted = new HashSet(invoice.GetPayments(coins.Wallet.Network).Select(p => p.Outpoint)); foreach (var coin in coins.TimestampedCoins.Where(c => !alreadyAccounted.Contains(c.Coin.Outpoint))) @@ -283,7 +273,7 @@ namespace BTCPayServer.HostedServices Strategy: d.DerivationStrategyBase)) .Where(d => d.Wallet != null) .Select(d => (Network: d.Network, - Coins: d.Wallet.GetCoins(d.Strategy, context.KnownStates.TryGet(d.Network)))) + Coins: d.Wallet.GetCoins(d.Strategy))) .Select(async d => { var coins = await d.Coins; @@ -442,7 +432,7 @@ namespace BTCPayServer.HostedServices _Loop = StartLoop(_Cts.Token); leases.Add(_EventAggregator.Subscribe(async b => { await NotifyBlock(); })); - leases.Add(_EventAggregator.Subscribe(async b => { await NotifyReceived(b.ScriptPubKey, b.Network); })); + leases.Add(_EventAggregator.Subscribe(async b => { await NotifyReceived(b); })); leases.Add(_EventAggregator.Subscribe(async b => { if (b.Name == "invoice_created") diff --git a/BTCPayServer/HostedServices/NBXplorerListener.cs b/BTCPayServer/HostedServices/NBXplorerListener.cs index 145d9e2c7..6cc913537 100644 --- a/BTCPayServer/HostedServices/NBXplorerListener.cs +++ b/BTCPayServer/HostedServices/NBXplorerListener.cs @@ -142,7 +142,8 @@ namespace BTCPayServer.HostedServices _Aggregator.Publish(new Events.TxOutReceivedEvent() { Network = network, - ScriptPubKey = txout.ScriptPubKey + ScriptPubKey = txout.ScriptPubKey, + DerivationStrategy = txout.DerivationStrategy }); } break; diff --git a/BTCPayServer/Services/Wallets/BTCPayWallet.cs b/BTCPayServer/Services/Wallets/BTCPayWallet.cs index 6bce43e8e..6f1e9f7a0 100644 --- a/BTCPayServer/Services/Wallets/BTCPayWallet.cs +++ b/BTCPayServer/Services/Wallets/BTCPayWallet.cs @@ -32,12 +32,16 @@ namespace BTCPayServer.Services.Wallets public class BTCPayWallet { private ExplorerClient _Client; - public BTCPayWallet(ExplorerClient client, BTCPayNetwork network) + private IMemoryCache _MemoryCache; + public BTCPayWallet(ExplorerClient client, IMemoryCache memoryCache, BTCPayNetwork network) { if (client == null) throw new ArgumentNullException(nameof(client)); + if (memoryCache == null) + throw new ArgumentNullException(nameof(memoryCache)); _Client = client; _Network = network; + _MemoryCache = memoryCache; } @@ -50,7 +54,7 @@ namespace BTCPayServer.Services.Wallets } } - public TimeSpan CacheSpan { get; private set; } = TimeSpan.FromMinutes(60); + public TimeSpan CacheSpan { get; private set; } = TimeSpan.FromMinutes(30); public async Task ReserveAddressAsync(DerivationStrategyBase derivationStrategy) { @@ -93,16 +97,25 @@ namespace BTCPayServer.Services.Wallets return tx; } - public async Task GetCoins(DerivationStrategyBase strategy, KnownState state, CancellationToken cancellation = default(CancellationToken)) + public void InvalidateCache(DerivationStrategyBase strategy) { - var changes = await _Client.GetUTXOsAsync(strategy, state?.PreviousCall, false, cancellation).ConfigureAwait(false); - return new NetworkCoins() + _MemoryCache.Remove("CACHEDCOINS_" + strategy.ToString()); + } + + public Task GetCoins(DerivationStrategyBase strategy, CancellationToken cancellation = default(CancellationToken)) + { + return _MemoryCache.GetOrCreateAsync("CACHEDCOINS_" + strategy.ToString(), async entry => { - TimestampedCoins = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).Select(c => new NetworkCoins.TimestampedCoin() { Coin = c.AsCoin(), DateTime = c.Timestamp }).ToArray(), - State = new KnownState() { PreviousCall = changes }, - Strategy = strategy, - Wallet = this - }; + entry.AbsoluteExpiration = DateTimeOffset.UtcNow + CacheSpan; + var changes = await _Client.GetUTXOsAsync(strategy, null, false, cancellation).ConfigureAwait(false); + return new NetworkCoins() + { + TimestampedCoins = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).Select(c => new NetworkCoins.TimestampedCoin() { Coin = c.AsCoin(), DateTime = c.Timestamp }).ToArray(), + State = new KnownState() { PreviousCall = changes }, + Strategy = strategy, + Wallet = this + }; + }); } public Task BroadcastTransactionsAsync(List transactions) @@ -111,6 +124,8 @@ namespace BTCPayServer.Services.Wallets return Task.WhenAll(tasks); } + + public async Task<(Coin[], Dictionary)> GetUnspentCoins(DerivationStrategyBase derivationStrategy, CancellationToken cancellation = default(CancellationToken)) { var changes = await _Client.GetUTXOsAsync(derivationStrategy, null, false, cancellation).ConfigureAwait(false); diff --git a/BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs b/BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs index 7fad2db05..f29b67538 100644 --- a/BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs +++ b/BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; namespace BTCPayServer.Services.Wallets { @@ -10,13 +11,16 @@ namespace BTCPayServer.Services.Wallets { private ExplorerClientProvider _Client; BTCPayNetworkProvider _NetworkProvider; + IOptions _Options; public BTCPayWalletProvider(ExplorerClientProvider client, + IOptions memoryCacheOption, BTCPayNetworkProvider networkProvider) { if (client == null) throw new ArgumentNullException(nameof(client)); _Client = client; _NetworkProvider = networkProvider; + _Options = memoryCacheOption; } public BTCPayWallet GetWallet(BTCPayNetwork network) @@ -33,7 +37,7 @@ namespace BTCPayServer.Services.Wallets var client = _Client.GetExplorerClient(cryptoCode); if (network == null || client == null) return null; - return new BTCPayWallet(client, network); + return new BTCPayWallet(client, new MemoryCache(_Options), network); } public bool IsAvailable(BTCPayNetwork network)