Some refactoring improving performance, and better tests for multiple currencies

This commit is contained in:
nicolas.dorier
2018-01-11 17:29:48 +09:00
parent 55d50af39d
commit 3a91965187
19 changed files with 253 additions and 153 deletions

View File

@@ -43,6 +43,8 @@ namespace BTCPayServer.Tests
{
get; set;
}
public Uri LTCNBXplorerUri { get; set; }
public string CookieFile
{
get; set;
@@ -79,7 +81,9 @@ namespace BTCPayServer.Tests
config.AppendLine($"regtest=1");
config.AppendLine($"port={Port}");
config.AppendLine($"explorer.url={NBXplorerUri.AbsoluteUri}");
config.AppendLine($"ltc.explorer.url={LTCNBXplorerUri.AbsoluteUri}");
config.AppendLine($"explorer.cookiefile={CookieFile}");
config.AppendLine($"ltc.explorer.cookiefile={CookieFile}");
config.AppendLine($"hdpubkey={HDPrivateKey.Neuter().ToString(Network.RegTest)}");
if (Postgres != null)
config.AppendLine($"postgres=" + Postgres);

View File

@@ -47,11 +47,17 @@ namespace BTCPayServer.Tests
Directory.CreateDirectory(_Directory);
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_RPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), Network);
var network = new BTCPayNetworkProvider(Network);
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_RPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), network.GetNetwork("BTC").NBitcoinNetwork);
LTCExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_LTCRPCCONNECTION", "server=http://127.0.0.1:43783;ceiwHEbqWI83:DwubwWsoo3")), network.GetNetwork("LTC").NBitcoinNetwork);
ExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_NBXPLORERURL", "http://127.0.0.1:32838/")));
LTCExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32839/")));
PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay"))
{
NBXplorerUri = ExplorerClient.Address,
LTCNBXplorerUri = LTCExplorerClient.Address,
Postgres = GetEnvironment("TESTS_POSTGRES", "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver")
};
PayTester.Port = int.Parse(GetEnvironment("TESTS_PORT", Utils.FreeTcpPort().ToString()));
@@ -107,10 +113,16 @@ namespace BTCPayServer.Tests
get; set;
}
public RPCClient LTCExplorerNode
{
get; set;
}
public ExplorerClient ExplorerClient
{
get; set;
}
public ExplorerClient LTCExplorerClient { get; set; }
HttpClient _Http = new HttpClient();

View File

@@ -71,6 +71,23 @@ namespace BTCPayServer.Tests
return store;
}
public void RegisterDerivationScheme(string crytoCode)
{
RegisterDerivationSchemeAsync(crytoCode).GetAwaiter().GetResult();
}
public async Task RegisterDerivationSchemeAsync(string crytoCode)
{
var store = parent.PayTester.GetController<StoresController>(UserId);
var networkProvider = parent.PayTester.GetService<BTCPayNetworkProvider>();
var derivation = new DerivationStrategyFactory(networkProvider.GetNetwork(crytoCode).NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]");
await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel()
{
CryptoCurrency = crytoCode,
DerivationSchemeFormat = crytoCode,
DerivationScheme = derivation.ToString(),
}, "Save");
}
public DerivationStrategyBase DerivationScheme { get; set; }
private async Task RegisterAsync()

View File

@@ -43,7 +43,7 @@ namespace BTCPayServer.Tests
entity.TxFee = Money.Coins(0.1m);
entity.Rate = 5000;
var cryptoData = entity.GetCryptoData("BTC");
var cryptoData = entity.GetCryptoData("BTC", null);
Assert.NotNull(cryptoData); // Should use legacy data to build itself
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
entity.ProductInformation = new ProductInformation() { Price = 5000 };
@@ -92,17 +92,17 @@ namespace BTCPayServer.Tests
})
}));
entity.Payments = new List<PaymentEntity>();
cryptoData = entity.GetCryptoData("BTC");
cryptoData = entity.GetCryptoData("BTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(5.1m), accounting.Due);
cryptoData = entity.GetCryptoData("LTC");
cryptoData = entity.GetCryptoData("LTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
cryptoData = entity.GetCryptoData("BTC");
cryptoData = entity.GetCryptoData("BTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(4.2m), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -110,7 +110,7 @@ namespace BTCPayServer.Tests
Assert.Equal(Money.Coins(5.2m), accounting.TotalDue);
Assert.Equal(2, accounting.TxCount);
cryptoData = entity.GetCryptoData("LTC");
cryptoData = entity.GetCryptoData("LTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 - 2.0m /* 8.21m */), accounting.Due);
Assert.Equal(Money.Coins(0.0m), accounting.CryptoPaid);
@@ -120,7 +120,7 @@ namespace BTCPayServer.Tests
entity.Payments.Add(new PaymentEntity() { CryptoCode = "LTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
cryptoData = entity.GetCryptoData("BTC");
cryptoData = entity.GetCryptoData("BTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(4.2m - 0.5m + 0.01m / 2), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -128,7 +128,7 @@ namespace BTCPayServer.Tests
Assert.Equal(Money.Coins(5.2m + 0.01m / 2), accounting.TotalDue); // The fee for LTC added
Assert.Equal(2, accounting.TxCount);
cryptoData = entity.GetCryptoData("LTC");
cryptoData = entity.GetCryptoData("LTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Coins(8.21m - 1.0m + 0.01m), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -139,7 +139,7 @@ namespace BTCPayServer.Tests
var remaining = Money.Coins(4.2m - 0.5m + 0.01m / 2);
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(remaining, new Key()), Accounted = true });
cryptoData = entity.GetCryptoData("BTC");
cryptoData = entity.GetCryptoData("BTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Zero, accounting.Due);
Assert.Equal(Money.Coins(1.0m) + remaining, accounting.CryptoPaid);
@@ -148,7 +148,7 @@ namespace BTCPayServer.Tests
Assert.Equal(accounting.Paid, accounting.TotalDue);
Assert.Equal(2, accounting.TxCount);
cryptoData = entity.GetCryptoData("LTC");
cryptoData = entity.GetCryptoData("LTC", null);
accounting = cryptoData.Calculate();
Assert.Equal(Money.Zero, accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -384,6 +384,93 @@ namespace BTCPayServer.Tests
}
}
[Fact]
public void CanPayWithTwoCurrencies()
{
using (var tester = ServerTester.Create())
{
tester.Start();
var user = tester.NewAccount();
user.GrantAccess();
// First we try payment with a merchant having only BTC
var invoice = user.BitPay.CreateInvoice(new Invoice()
{
Price = 5000.0,
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
ItemDesc = "Some description",
FullNotifications = true
}, Facade.Merchant);
var cashCow = tester.ExplorerNode;
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
var firstPayment = Money.Coins(0.04m);
cashCow.SendToAddress(invoiceAddress, firstPayment);
Eventually(() =>
{
invoice = user.BitPay.GetInvoice(invoice.Id);
Assert.True(invoice.BtcPaid == firstPayment);
});
Assert.Single(invoice.CryptoInfo); // Only BTC should be presented
var controller = tester.PayTester.GetController<InvoiceController>(null);
var checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, null).GetAwaiter().GetResult()).Value;
Assert.Single(checkout.AvailableCryptos);
Assert.Equal("BTC", checkout.CryptoCode);
//////////////////////
// Retry now with LTC enabled
user.RegisterDerivationScheme("LTC");
invoice = user.BitPay.CreateInvoice(new Invoice()
{
Price = 5000.0,
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
ItemDesc = "Some description",
FullNotifications = true
}, Facade.Merchant);
cashCow = tester.ExplorerNode;
invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
firstPayment = Money.Coins(0.04m);
cashCow.SendToAddress(invoiceAddress, firstPayment);
Eventually(() =>
{
invoice = user.BitPay.GetInvoice(invoice.Id);
Assert.True(invoice.BtcPaid == firstPayment);
});
cashCow = tester.LTCExplorerNode;
var ltcCryptoInfo = invoice.CryptoInfo.FirstOrDefault(c => c.CryptoCode == "LTC");
Assert.NotNull(ltcCryptoInfo);
invoiceAddress = BitcoinAddress.Create(ltcCryptoInfo.Address, cashCow.Network);
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);
Eventually(() =>
{
invoice = user.BitPay.GetInvoice(invoice.Id);
Assert.Equal(Money.Zero, invoice.BtcDue);
var ltcPaid = invoice.CryptoInfo.First(c => c.CryptoCode == "LTC");
Assert.Equal(Money.Zero, ltcPaid.Due);
Assert.Equal(secondPayment, ltcPaid.CryptoPaid);
Assert.Equal("paid", invoice.Status);
Assert.False((bool)((JValue)invoice.ExceptionStatus).Value);
});
controller = tester.PayTester.GetController<InvoiceController>(null);
checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, "LTC").GetAwaiter().GetResult()).Value;
Assert.Equal(2, checkout.AvailableCryptos.Count);
Assert.Equal("LTC", checkout.CryptoCode);
}
}
[Fact]
public void InvoiceFlowThroughDifferentStatesCorrectly()
{
@@ -398,8 +485,6 @@ namespace BTCPayServer.Tests
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
//RedirectURL = redirect + "redirect",
//NotificationURL = CallbackUri + "/notification",
ItemDesc = "Some description",
FullNotifications = true
}, Facade.Merchant);

View File

@@ -11,7 +11,8 @@ services:
dockerfile: BTCPayServer.Tests/Dockerfile
environment:
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
TESTS_BTCNBXPLORERURL: http://bitcoin-nbxplorer:32838/
TESTS_LTCRPCCONNECTION: server=http://litecoind:43782;ceiwHEbqWI83:DwubwWsoo3
TESTS_NBXPLORERURL: http://bitcoin-nbxplorer:32838/
TESTS_LTCNBXPLORERURL: http://litecoin-nbxplorer:32839/
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
TESTS_PORT: 80

View File

@@ -25,5 +25,9 @@ namespace BTCPayServer
}
public string CryptoImagePath { get; set; }
public override string ToString()
{
return CryptoCode;
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.0.0.64</Version>
<Version>1.0.0.65</Version>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Build\dockerfiles\**" />
@@ -22,7 +22,7 @@
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="NBitcoin" Version="4.0.0.51" />
<PackageReference Include="NBitpayClient" Version="1.0.0.14" />
<PackageReference Include="NBitpayClient" Version="1.0.0.16" />
<PackageReference Include="DBreeze" Version="1.87.0" />
<PackageReference Include="NBXplorer.Client" Version="1.0.0.28" />
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />

View File

@@ -46,7 +46,7 @@ namespace BTCPayServer.Configuration
Logs.Configuration.LogInformation("Network: " + Network);
bool btcHandled = false;
foreach (var net in new BTCPayNetworkProvider(Network).GetAll())
{
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(net.NBitcoinNetwork.Name);
@@ -54,12 +54,16 @@ namespace BTCPayServer.Configuration
var cookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", nbxplorer.GetDefaultCookieFile());
if (explorer != null)
{
#pragma warning disable CS0618
if (net.IsBTC)
btcHandled = true;
#pragma warning restore CS0618
ExplorerFactories.Add(net.CryptoCode, (n) => CreateExplorerClient(n, explorer, cookieFile));
}
}
// Handle legacy explorer.url and explorer.cookiefile
if (ExplorerFactories.Count == 0)
if (!btcHandled)
{
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(Network.Name); // Will get BTC info
var explorer = conf.GetOrDefault<Uri>($"explorer.url", new Uri(nbxplorer.GetDefaultExplorerUrl(), UriKind.Absolute));

View File

@@ -59,7 +59,7 @@ namespace BTCPayServer.Controllers
StatusException = invoice.ExceptionStatus
};
foreach (var data in invoice.GetCryptoData())
foreach (var data in invoice.GetCryptoData(null))
{
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(data.Key, StringComparison.OrdinalIgnoreCase));
var accounting = data.Value.Calculate();
@@ -128,7 +128,7 @@ namespace BTCPayServer.Controllers
var network = _NetworkProvider.GetNetwork(cryptoCode);
if (invoice == null || network == null || !invoice.Support(network))
return null;
var cryptoData = invoice.GetCryptoData(network);
var cryptoData = invoice.GetCryptoData(network, _NetworkProvider);
var dto = invoice.EntityToDTO(_NetworkProvider);
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode == network.CryptoCode);
@@ -157,12 +157,15 @@ namespace BTCPayServer.Controllers
Status = invoice.Status,
CryptoImage = "/" + Url.Content(network.CryptoImagePath),
NetworkFeeDescription = $"{accounting.TxCount} transaction{(accounting.TxCount > 1 ? "s" : "")} x {cryptoData.TxFee} {network.CryptoCode}",
AvailableCryptos = invoice.GetCryptoData().Select(kv=> new PaymentModel.AvailableCrypto()
AvailableCryptos = invoice.GetCryptoData(_NetworkProvider)
.Where(i => i.Value.Network != null)
.Select(kv=> new PaymentModel.AvailableCrypto()
{
CryptoCode = kv.Key,
CryptoImage = "/" + _NetworkProvider.GetNetwork(kv.Key).CryptoImagePath,
CryptoImage = "/" + kv.Value.Network.CryptoImagePath,
Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, cryptoCode = kv.Key })
}).ToList()
}).Where(c => c.CryptoImage != "/")
.ToList()
};
var isMultiCurrency = invoice.GetPayments().Select(p=>p.GetCryptoCode()).Concat(new[] { network.CryptoCode }).Distinct().Count() > 1;

View File

@@ -121,7 +121,9 @@ namespace BTCPayServer.Controllers
Network: derivationStrategy.Network,
RateProvider: _RateProviders.GetRateProvider(derivationStrategy.Network),
FeeRateProvider: _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network)))
.Where(_ => _.Wallet != null && _.FeeRateProvider != null && _.RateProvider != null)
.Where(_ => _.Wallet != null &&
_.FeeRateProvider != null &&
_.RateProvider != null)
.Select(_ =>
{
return new

View File

@@ -14,7 +14,7 @@ namespace BTCPayServer.Events
public override string ToString()
{
String address = ScriptPubKey.GetDestinationAddress(Network.NBitcoinNetwork)?.ToString() ?? ScriptPubKey.ToString();
return $"{address} received a transaction";
return $"{address} received a transaction ({Network.CryptoCode})";
}
}
}

View File

@@ -37,11 +37,11 @@ namespace BTCPayServer
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static async Task<Dictionary<uint256, TransactionResult>> GetTransactions(this BTCPayWallet client, BTCPayNetwork network, uint256[] hashes, CancellationToken cts = default(CancellationToken))
public static async Task<Dictionary<uint256, TransactionResult>> GetTransactions(this BTCPayWallet client, uint256[] hashes, CancellationToken cts = default(CancellationToken))
{
hashes = hashes.Distinct().ToArray();
var transactions = hashes
.Select(async o => await client.GetTransactionAsync(network, o, cts))
.Select(async o => await client.GetTransactionAsync(o, cts))
.ToArray();
await Task.WhenAll(transactions).ConfigureAwait(false);
return transactions.Select(t => t.Result).Where(t => t != null).ToDictionary(o => o.Transaction.GetHash());

View File

@@ -150,7 +150,7 @@ namespace BTCPayServer.HostedServices
}
var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
var payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
var payments = await GetPaymentsWithTransaction(null, derivationStrategies, invoice);
foreach (Task<NetworkCoins> coinsAsync in GetCoinsPerNetwork(context, invoice, derivationStrategies))
{
var coins = await coinsAsync;
@@ -173,11 +173,11 @@ namespace BTCPayServer.HostedServices
}
if (dirtyAddress)
{
payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
payments = await GetPaymentsWithTransaction(payments, derivationStrategies, invoice);
}
var network = coins.Wallet.Network;
var cryptoData = invoice.GetCryptoData(network);
var cryptoDataAll = invoice.GetCryptoData();
var cryptoData = invoice.GetCryptoData(network, _NetworkProvider);
var cryptoDataAll = invoice.GetCryptoData(_NetworkProvider);
var accounting = cryptoData.Calculate();
if (invoice.Status == "new" || invoice.Status == "expired")
@@ -222,7 +222,7 @@ namespace BTCPayServer.HostedServices
if (invoice.Status == "paid")
{
var transactions = payments;
IEnumerable<AccountedPaymentEntity> transactions = payments;
if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
{
transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
@@ -260,7 +260,7 @@ namespace BTCPayServer.HostedServices
if (invoice.Status == "confirmed")
{
var transactions = payments;
IEnumerable<AccountedPaymentEntity> transactions = payments;
transactions = transactions.Where(t => t.Confirmations >= 6);
var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
if (totalConfirmed >= accounting.TotalDue)
@@ -292,21 +292,59 @@ namespace BTCPayServer.HostedServices
.ToArray();
}
private async Task<IEnumerable<AccountedPaymentEntity>> GetPaymentsWithTransaction(DerivationStrategy[] derivations, InvoiceEntity invoice)
class AccountedPaymentEntities : List<AccountedPaymentEntity>
{
public AccountedPaymentEntities(AccountedPaymentEntities existing)
{
if (existing != null)
_Transactions = existing._Transactions;
}
Dictionary<uint256, TransactionResult> _Transactions = new Dictionary<uint256, TransactionResult>();
public void AddToCache(IEnumerable<TransactionResult> transactions)
{
foreach (var tx in transactions)
_Transactions.TryAdd(tx.Transaction.GetHash(), tx);
}
public TransactionResult GetTransaction(uint256 txId)
{
_Transactions.TryGetValue(txId, out TransactionResult result);
return result;
}
internal IEnumerable<TransactionResult> GetTransactions()
{
return _Transactions.Values;
}
}
private async Task<AccountedPaymentEntities> GetPaymentsWithTransaction(AccountedPaymentEntities previous, DerivationStrategy[] derivations, InvoiceEntity invoice)
{
List<PaymentEntity> updatedPaymentEntities = new List<PaymentEntity>();
List<AccountedPaymentEntity> accountedPayments = new List<AccountedPaymentEntity>();
AccountedPaymentEntities accountedPayments = new AccountedPaymentEntities(previous);
foreach (var network in derivations.Select(d => d.Network))
{
var wallet = _WalletProvider.GetWallet(network);
if (wallet == null)
continue;
var transactions = await wallet.GetTransactions(network, invoice.GetPayments(network).Select(t => t.Outpoint.Hash).ToArray());
var conflicts = GetConflicts(transactions.Select(t => t.Value));
var hashesToFetch = new HashSet<uint256>(invoice
.GetPayments(network)
.Select(t => t.Outpoint.Hash)
.Where(h => accountedPayments?.GetTransaction(h) == null)
.ToList());
if (hashesToFetch.Count > 0)
{
accountedPayments.AddToCache((await wallet.GetTransactions(hashesToFetch.ToArray())).Select(t => t.Value));
}
var conflicts = GetConflicts(accountedPayments.GetTransactions());
foreach (var payment in invoice.GetPayments(network))
{
TransactionResult tx;
if (!transactions.TryGetValue(payment.Outpoint.Hash, out tx))
TransactionResult tx = accountedPayments.GetTransaction(payment.Outpoint.Hash);
if (tx == null)
continue;
AccountedPaymentEntity accountedPayment = new AccountedPaymentEntity()

View File

@@ -37,81 +37,6 @@ namespace BTCPayServer.Models
}
}
public class InvoiceCryptoInfo
{
[JsonProperty("cryptoCode")]
public string CryptoCode { get; set; }
[JsonProperty("rate")]
public decimal Rate { get; set; }
//"exRates":{"USD":4320.02}
[JsonProperty("exRates")]
public Dictionary<string, double> ExRates
{
get; set;
}
//"btcPaid":"0.000000"
[JsonProperty("paid")]
public string Paid
{
get; set;
}
//"btcPrice":"0.001157"
[JsonProperty("price")]
public string Price
{
get; set;
}
//"btcDue":"0.001160"
/// <summary>
/// Amount of crypto remaining to pay this invoice
/// </summary>
[JsonProperty("due")]
public string Due
{
get; set;
}
[JsonProperty("paymentUrls")]
public NBitpayClient.InvoicePaymentUrls PaymentUrls
{
get; set;
}
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
/// <summary>
/// Total amount of this invoice
/// </summary>
[JsonProperty("totalDue")]
public string TotalDue { get; set; }
/// <summary>
/// Total amount of network fee to pay to the invoice
/// </summary>
[JsonProperty("networkFee")]
public string NetworkFee { get; set; }
/// <summary>
/// Number of transactions required to pay
/// </summary>
[JsonProperty("txCount")]
public int TxCount { get; set; }
/// <summary>
/// Total amount of the invoice paid in this crypto
/// </summary>
[JsonProperty("cryptoPaid")]
public Money CryptoPaid { get; set; }
}
//{"facade":"pos/invoice","data":{,}}
public class InvoiceResponse
{
@@ -151,7 +76,7 @@ namespace BTCPayServer.Models
}
[JsonProperty("cryptoInfo")]
public List<InvoiceCryptoInfo> CryptoInfo { get; set; }
public List<NBitpayClient.InvoiceCryptoInfo> CryptoInfo { get; set; }
//"price":5
[JsonProperty("price")]

View File

@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Text;
using BTCPayServer.Models;
using NBitpayClient;
using Newtonsoft.Json.Linq;
using NBitcoin.DataEncoders;
using BTCPayServer.Data;
@@ -234,7 +233,7 @@ namespace BTCPayServer.Services.Invoices
}
public List<PaymentEntity> GetPayments(string cryptoCode)
{
return Payments.Where(p=>p.CryptoCode == cryptoCode).ToList();
return Payments.Where(p => p.CryptoCode == cryptoCode).ToList();
}
public List<PaymentEntity> GetPayments(BTCPayNetwork network)
{
@@ -323,11 +322,11 @@ namespace BTCPayServer.Services.Invoices
Flags = new Flags() { Refundable = Refundable }
};
dto.CryptoInfo = new List<InvoiceCryptoInfo>();
foreach (var info in this.GetCryptoData().Values)
dto.CryptoInfo = new List<NBitpayClient.InvoiceCryptoInfo>();
foreach (var info in this.GetCryptoData(networkProvider).Values)
{
var accounting = info.Calculate();
var cryptoInfo = new InvoiceCryptoInfo();
var cryptoInfo = new NBitpayClient.InvoiceCryptoInfo();
cryptoInfo.CryptoCode = info.CryptoCode;
cryptoInfo.Rate = info.Rate;
cryptoInfo.Price = Money.Coins(ProductInformation.Price / cryptoInfo.Rate).ToString();
@@ -337,7 +336,7 @@ namespace BTCPayServer.Services.Invoices
cryptoInfo.TotalDue = accounting.TotalDue.ToString();
cryptoInfo.NetworkFee = accounting.NetworkFee.ToString();
cryptoInfo.TxCount = accounting.TxCount;
cryptoInfo.CryptoPaid = accounting.CryptoPaid;
cryptoInfo.CryptoPaid = accounting.CryptoPaid.ToString();
cryptoInfo.Address = info.DepositAddress;
cryptoInfo.ExRates = new Dictionary<string, double>
@@ -345,12 +344,12 @@ namespace BTCPayServer.Services.Invoices
{ ProductInformation.Currency, (double)cryptoInfo.Rate }
};
var scheme = networkProvider.GetNetwork(info.CryptoCode)?.UriScheme ?? "BTC";
var scheme = info.Network.UriScheme;
var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode;
cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"invoice{cryptoSuffix}?id=" + Id;
cryptoInfo.PaymentUrls = new InvoicePaymentUrls()
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
{
BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
BIP72b = $"{scheme}:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
@@ -392,23 +391,23 @@ namespace BTCPayServer.Services.Invoices
internal bool Support(BTCPayNetwork network)
{
var rates = GetCryptoData();
var rates = GetCryptoData(null);
return rates.TryGetValue(network.CryptoCode, out var data);
}
public CryptoData GetCryptoData(string cryptoCode)
public CryptoData GetCryptoData(string cryptoCode, BTCPayNetworkProvider networkProvider)
{
GetCryptoData().TryGetValue(cryptoCode, out var data);
GetCryptoData(networkProvider).TryGetValue(cryptoCode, out var data);
return data;
}
public CryptoData GetCryptoData(BTCPayNetwork network)
public CryptoData GetCryptoData(BTCPayNetwork network, BTCPayNetworkProvider networkProvider)
{
GetCryptoData().TryGetValue(network.CryptoCode, out var data);
GetCryptoData(networkProvider).TryGetValue(network.CryptoCode, out var data);
return data;
}
public Dictionary<string, CryptoData> GetCryptoData()
public Dictionary<string, CryptoData> GetCryptoData(BTCPayNetworkProvider networkProvider)
{
Dictionary<string, CryptoData> rates = new Dictionary<string, CryptoData>();
var serializer = new Serializer(Dummy);
@@ -416,7 +415,8 @@ namespace BTCPayServer.Services.Invoices
// Legacy
if (Rate != 0.0m)
{
rates.TryAdd("BTC", new CryptoData() { ParentEntity = this, Rate = Rate, CryptoCode = "BTC", TxFee = TxFee, FeeRate = new FeeRate(TxFee, 100), DepositAddress = DepositAddress });
var btcNetwork = networkProvider?.GetNetwork("BTC");
rates.TryAdd("BTC", new CryptoData() { ParentEntity = this, Rate = Rate, CryptoCode = "BTC", TxFee = TxFee, FeeRate = new FeeRate(TxFee, 100), DepositAddress = DepositAddress, Network = btcNetwork });
}
if (CryptoData != null)
{
@@ -425,6 +425,7 @@ namespace BTCPayServer.Services.Invoices
var r = serializer.ToObject<CryptoData>(prop.Value.ToString());
r.CryptoCode = prop.Name;
r.ParentEntity = this;
r.Network = networkProvider?.GetNetwork(r.CryptoCode);
rates.TryAdd(r.CryptoCode, r);
}
}
@@ -433,6 +434,14 @@ namespace BTCPayServer.Services.Invoices
}
Network Dummy = Network.Main;
public void SetCryptoData(CryptoData cryptoData)
{
var dict = GetCryptoData(null);
dict.AddOrReplace(cryptoData.CryptoCode, cryptoData);
SetCryptoData(dict);
}
public void SetCryptoData(Dictionary<string, CryptoData> cryptoData)
{
var obj = new JObject();
@@ -485,6 +494,8 @@ namespace BTCPayServer.Services.Invoices
{
[JsonIgnore]
public InvoiceEntity ParentEntity { get; set; }
[JsonIgnore]
public BTCPayNetwork Network { get; set; }
[JsonProperty(PropertyName = "cryptoCode", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string CryptoCode { get; set; }
[JsonProperty(PropertyName = "rate")]
@@ -498,7 +509,7 @@ namespace BTCPayServer.Services.Invoices
public CryptoDataAccounting Calculate()
{
var cryptoData = ParentEntity.GetCryptoData();
var cryptoData = ParentEntity.GetCryptoData(null);
var totalDue = Money.Coins(ParentEntity.ProductInformation.Price / Rate);
var paid = Money.Zero;
var cryptoPaid = Money.Zero;

View File

@@ -125,16 +125,15 @@ namespace BTCPayServer.Services.Invoices
CustomerEmail = invoice.RefundMail
});
foreach (var cryptoData in invoice.GetCryptoData().Values)
foreach (var cryptoData in invoice.GetCryptoData(networkProvider).Values)
{
var network = networkProvider.GetNetwork(cryptoData.CryptoCode);
if (network == null)
if (cryptoData.Network == null)
throw new InvalidOperationException("CryptoCode unsupported");
context.AddressInvoices.Add(new AddressInvoiceData()
{
InvoiceDataId = invoice.Id,
CreatedTime = DateTimeOffset.UtcNow,
}.SetHash(BitcoinAddress.Create(cryptoData.DepositAddress, network.NBitcoinNetwork).ScriptPubKey.Hash, network.CryptoCode));
}.SetHash(BitcoinAddress.Create(cryptoData.DepositAddress, cryptoData.Network.NBitcoinNetwork).ScriptPubKey.Hash, cryptoData.CryptoCode));
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
{
InvoiceDataId = invoice.Id,
@@ -169,8 +168,7 @@ namespace BTCPayServer.Services.Invoices
return false;
var invoiceEntity = ToObject<InvoiceEntity>(invoice.Blob);
var cryptoData = invoiceEntity.GetCryptoData();
var currencyData = cryptoData.Where(c => c.Value.CryptoCode == network.CryptoCode).Select(f => f.Value).FirstOrDefault();
var currencyData = invoiceEntity.GetCryptoData(network, null);
if (currencyData == null)
return false;
@@ -187,7 +185,7 @@ namespace BTCPayServer.Services.Invoices
invoiceEntity.DepositAddress = currencyData.DepositAddress;
}
#pragma warning restore CS0618
invoiceEntity.SetCryptoData(cryptoData);
invoiceEntity.SetCryptoData(currencyData);
invoice.Blob = ToBytes(invoiceEntity);
context.AddressInvoices.Add(new AddressInvoiceData() {
@@ -207,7 +205,7 @@ namespace BTCPayServer.Services.Invoices
private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, string cryptoCode)
{
foreach (var address in entity.GetCryptoData())
foreach (var address in entity.GetCryptoData(null))
{
if (cryptoCode != null && cryptoCode != address.Value.CryptoCode)
continue;

View File

@@ -38,12 +38,6 @@ namespace BTCPayServer.Services.Rates
});
}
private bool TryGetFromCache(string key, out object obj)
{
obj = _MemoryCache.Get(key);
return obj != null;
}
public Task<ICollection<Rate>> GetRatesAsync()
{
return _MemoryCache.GetOrCreateAsync("GLOBAL_RATES", (ICacheEntry entry) =>

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using BTCPayServer.Data;
using System.Threading;
using NBXplorer.Models;
using Microsoft.Extensions.Caching.Memory;
namespace BTCPayServer.Services.Wallets
{
@@ -51,6 +52,8 @@ namespace BTCPayServer.Services.Wallets
}
}
public TimeSpan CacheSpan { get; private set; } = TimeSpan.FromMinutes(60);
public async Task<BitcoinAddress> ReserveAddressAsync(DerivationStrategyBase derivationStrategy)
{
var pathInfo = await _Client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
@@ -62,10 +65,8 @@ namespace BTCPayServer.Services.Wallets
await _Client.TrackAsync(derivationStrategy);
}
public Task<TransactionResult> GetTransactionAsync(BTCPayNetwork network, uint256 txId, CancellationToken cancellation = default(CancellationToken))
public Task<TransactionResult> GetTransactionAsync(uint256 txId, CancellationToken cancellation = default(CancellationToken))
{
if (network == null)
throw new ArgumentNullException(nameof(network));
if (txId == null)
throw new ArgumentNullException(nameof(txId));
return _Client.GetTransactionAsync(txId, cancellation);

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
namespace BTCPayServer.Services.Wallets
{
@@ -29,7 +30,7 @@ namespace BTCPayServer.Services.Wallets
throw new ArgumentNullException(nameof(cryptoCode));
var network = _NetworkProvider.GetNetwork(cryptoCode);
var client = _Client.GetExplorerClient(cryptoCode);
if (network == null && client == null)
if (network == null || client == null)
return null;
return new BTCPayWallet(client, network);
}