Caching GetCoins

This commit is contained in:
nicolas.dorier
2018-02-15 13:02:12 +09:00
parent 17069c311b
commit 01d898b618
6 changed files with 45 additions and 33 deletions

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.0.1.26</Version>
<Version>1.0.1.27</Version>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>

View File

@@ -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()
{

View File

@@ -29,9 +29,6 @@ namespace BTCPayServer.HostedServices
{
}
public Dictionary<BTCPayNetwork, KnownState> KnownStates { get; set; }
public Dictionary<BTCPayNetwork, KnownState> ModifiedKnownStates { get; set; } = new Dictionary<BTCPayNetwork, KnownState>();
public InvoiceEntity Invoice { get; set; }
public List<object> Events { get; set; } = new List<object>();
@@ -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<OutPoint>(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<Events.NewBlockEvent>(async b => { await NotifyBlock(); }));
leases.Add(_EventAggregator.Subscribe<Events.TxOutReceivedEvent>(async b => { await NotifyReceived(b.ScriptPubKey, b.Network); }));
leases.Add(_EventAggregator.Subscribe<Events.TxOutReceivedEvent>(async b => { await NotifyReceived(b); }));
leases.Add(_EventAggregator.Subscribe<Events.InvoiceEvent>(async b =>
{
if (b.Name == "invoice_created")

View File

@@ -142,7 +142,8 @@ namespace BTCPayServer.HostedServices
_Aggregator.Publish(new Events.TxOutReceivedEvent()
{
Network = network,
ScriptPubKey = txout.ScriptPubKey
ScriptPubKey = txout.ScriptPubKey,
DerivationStrategy = txout.DerivationStrategy
});
}
break;

View File

@@ -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<BitcoinAddress> ReserveAddressAsync(DerivationStrategyBase derivationStrategy)
{
@@ -93,16 +97,25 @@ namespace BTCPayServer.Services.Wallets
return tx;
}
public async Task<NetworkCoins> 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<NetworkCoins> 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<BroadcastResult[]> BroadcastTransactionsAsync(List<Transaction> transactions)
@@ -111,6 +124,8 @@ namespace BTCPayServer.Services.Wallets
return Task.WhenAll(tasks);
}
public async Task<(Coin[], Dictionary<Script, KeyPath>)> GetUnspentCoins(DerivationStrategyBase derivationStrategy, CancellationToken cancellation = default(CancellationToken))
{
var changes = await _Client.GetUTXOsAsync(derivationStrategy, null, false, cancellation).ConfigureAwait(false);

View File

@@ -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<MemoryCacheOptions> _Options;
public BTCPayWalletProvider(ExplorerClientProvider client,
IOptions<MemoryCacheOptions> 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)