Add litecoin to docker-compose fix bugs when two networks generate same address

This commit is contained in:
nicolas.dorier 2018-01-09 01:56:37 +09:00
parent a048494f34
commit 31672a2587
10 changed files with 136 additions and 83 deletions

View file

@ -383,9 +383,9 @@ namespace BTCPayServer.Tests
Assert.True(IsMapped(localInvoice, ctx));
invoiceEntity = repo.GetInvoice(null, invoice.Id, true).GetAwaiter().GetResult();
var historical1 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.Address == invoice.BitcoinAddress.ToString());
var historical1 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == invoice.BitcoinAddress.ToString());
Assert.NotNull(historical1.UnAssigned);
var historical2 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.Address == localInvoice.BitcoinAddress.ToString());
var historical2 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == localInvoice.BitcoinAddress.ToString());
Assert.Null(historical2.UnAssigned);
invoiceAddress = BitcoinAddress.Create(localInvoice.BitcoinAddress, cashCow.Network);
secondPayment = localInvoice.BtcDue;
@ -473,8 +473,8 @@ namespace BTCPayServer.Tests
private static bool IsMapped(Invoice invoice, ApplicationDbContext ctx)
{
var h = BitcoinAddress.Create(invoice.BitcoinAddress).ScriptPubKey.Hash.ToString();
return ctx.AddressInvoices.FirstOrDefault(i => i.InvoiceDataId == invoice.Id && i.Address == h) != null;
var h = BitcoinAddress.Create(invoice.BitcoinAddress).ScriptPubKey.Hash;
return ctx.AddressInvoices.FirstOrDefault(i => i.InvoiceDataId == invoice.Id && i.GetHash() == h) != null;
}
private void Eventually(Action act)

View file

@ -11,19 +11,17 @@ services:
dockerfile: BTCPayServer.Tests/Dockerfile
environment:
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
TESTS_NBXPLORERURL: http://nbxplorer:32838/
TESTS_BTCNBXPLORERURL: http://bitcoin-nbxplorer:32838/
TESTS_LTCNBXPLORERURL: http://litecoin-nbxplorer:32839/
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
TESTS_PORT: 80
TESTS_HOSTNAME: tests
# TEST_ECLAIR1: http://eclair1:8080/
# TEST_ECLAIR2: http://eclair2:8080/
expose:
- "80"
links:
- bitcoind
- nbxplorer
# - eclair1
# - eclair2
- bitcoin-nbxplorer
- litecoin-nbxplorer
- postgres
extra_hosts:
- "tests:127.0.0.1"
@ -35,12 +33,11 @@ services:
regtest=1
connect=bitcoind:39388
links:
- bitcoind
- nbxplorer
# - eclair1
# - eclair2
- bitcoin-nbxplorer
- litecoin-nbxplorer
- postgres
nbxplorer:
bitcoin-nbxplorer:
image: nicolasdorier/nbxplorer:1.0.0.39
ports:
- "32838:32838"
@ -57,49 +54,24 @@ services:
NBXPLORER_NOAUTH: 1
links:
- bitcoind
- postgres
eclair1:
image: acinq/eclair:latest
environment:
JAVA_OPTS: >
-Xmx512m
-Declair.printToConsole
-Declair.bitcoind.host=bitcoind
-Declair.bitcoind.rpcport=43782
-Declair.bitcoind.rpcuser=ceiwHEbqWI83
-Declair.bitcoind.rpcpassword=DwubwWsoo3
-Declair.bitcoind.zmq=tcp://bitcoind:29000
-Declair.chain=regtest
-Declair.api.binding-ip=0.0.0.0
links:
- bitcoind
litecoin-nbxplorer:
image: nicolasdorier/nbxplorer:1.0.0.39
ports:
- "30992:8080" # api port
expose:
- "9735" # server port
- "8080" # api port
eclair2:
image: acinq/eclair:latest
- "32839:32839"
expose:
- "32839"
environment:
JAVA_OPTS: >
-Xmx512m
-Declair.printToConsole
-Declair.bitcoind.host=bitcoind
-Declair.bitcoind.rpcport=43782
-Declair.bitcoind.rpcuser=ceiwHEbqWI83
-Declair.bitcoind.rpcpassword=DwubwWsoo3
-Declair.bitcoind.zmq=tcp://bitcoind:29000
-Declair.chain=regtest
-Declair.api.binding-ip=0.0.0.0
NBXPLORER_NETWORK: ltc-regtest
NBXPLORER_RPCURL: http://litecoind:43782/
NBXPLORER_RPCUSER: ceiwHEbqWI83
NBXPLORER_RPCPASSWORD: DwubwWsoo3
NBXPLORER_NODEENDPOINT: litecoind:39388
NBXPLORER_BIND: 0.0.0.0:32839
NBXPLORER_VERBOSE: 1
NBXPLORER_NOAUTH: 1
links:
- bitcoind
ports:
- "30993:8080" # api port
expose:
- "9735" # server port
- "8080" # api port
- litecoind
bitcoind:
container_name: btcpayserver_dev_bitcoind
@ -116,8 +88,30 @@ services:
zmqpubrawblock=tcp://0.0.0.0:29000
zmqpubrawtx=tcp://0.0.0.0:29000
txindex=1
ports:
- "43782:43782" # RPC
ports:
- "43782:43782"
expose:
- "43782" # RPC
- "39388" # P2P
- "29000" # zmq
litecoind:
container_name: btcpayserver_dev_litecoind
image: nicolasdorier/docker-litecoin:0.14.2
environment:
BITCOIN_EXTRA_ARGS: |
rpcuser=ceiwHEbqWI83
rpcpassword=DwubwWsoo3
regtest=1
server=1
rpcport=43782
port=39388
whitelist=0.0.0.0/0
zmqpubrawblock=tcp://0.0.0.0:29000
zmqpubrawtx=tcp://0.0.0.0:29000
txindex=1
ports:
- "43783:43782"
expose:
- "43782" # RPC
- "39388" # P2P

View file

@ -0,0 +1 @@
docker exec -ti btcpayserver_dev_litecoind litecoin-cli -regtest -conf="/data/litecoin.conf" -datadir="/data" $args

View file

@ -45,11 +45,14 @@ namespace BTCPayServer.Configuration
DataDir = conf.GetOrDefault<string>("datadir", networkInfo.DefaultDataDirectory);
Logs.Configuration.LogInformation("Network: " + Network);
foreach (var net in new BTCPayNetworkProvider(Network).GetAll())
{
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(net.NBitcoinNetwork.Name);
var explorer = conf.GetOrDefault<Uri>($"{net.CryptoCode}.explorer.url", null);
var cookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", null);
if (explorer != null && cookieFile != null)
var cookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", nbxplorer.GetDefaultCookieFile());
if (explorer != null)
{
ExplorerFactories.Add(net.CryptoCode, (n) => CreateExplorerClient(n, explorer, cookieFile));
}
@ -58,7 +61,7 @@ namespace BTCPayServer.Configuration
// Handle legacy explorer.url and explorer.cookiefile
if (ExplorerFactories.Count == 0)
{
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(Network.Name);
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));
var cookieFile = conf.GetOrDefault<string>($"explorer.cookiefile", nbxplorer.GetDefaultCookieFile());
ExplorerFactories.Add("BTC", (n) => CreateExplorerClient(n, explorer, cookieFile));

View file

@ -2,16 +2,49 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
namespace BTCPayServer.Data
{
public class AddressInvoiceData
{
/// <summary>
/// Some crypto currencies share same address prefix
/// For not having exceptions thrown by two address on different network, we suffix by "#CRYPTOCODE"
/// </summary>
[Obsolete("Use GetHash instead")]
public string Address
{
get; set;
}
#pragma warning disable CS0618
public ScriptId GetHash()
{
if (Address == null)
return null;
var index = Address.IndexOf("#");
if (index == -1)
return new ScriptId(Address);
return new ScriptId(Address.Substring(0, index));
}
public AddressInvoiceData SetHash(ScriptId scriptId, string cryptoCode)
{
Address = scriptId + "#" + cryptoCode;
return this;
}
public string GetCryptoCode()
{
if (Address == null)
return null;
var index = Address.IndexOf("#");
if (index == -1)
return "BTC";
return Address.Substring(index + 1);
}
#pragma warning restore CS0618
public InvoiceData InvoiceData
{
get; set;
@ -26,5 +59,6 @@ namespace BTCPayServer.Data
{
get; set;
}
}
}

View file

@ -113,7 +113,9 @@ namespace BTCPayServer.Data
.HasForeignKey(pt => pt.StoreDataId);
builder.Entity<AddressInvoiceData>()
#pragma warning disable CS0618
.HasKey(o => o.Address);
#pragma warning restore CS0618
builder.Entity<PairingCodeData>()
.HasKey(o => o.Id);
@ -128,7 +130,9 @@ namespace BTCPayServer.Data
.HasKey(o => new
{
o.InvoiceDataId,
#pragma warning disable CS0618
o.Address
#pragma warning restore CS0618
});
}
}

View file

@ -12,6 +12,11 @@ namespace BTCPayServer.Data
get; set;
}
/// <summary>
/// Some crypto currencies share same address prefix
/// For not having exceptions thrown by two address on different network, we suffix by "#CRYPTOCODE"
/// </summary>
[Obsolete("Use GetCryptoCode instead")]
public string Address
{
get; set;
@ -26,6 +31,21 @@ namespace BTCPayServer.Data
{
return string.IsNullOrEmpty(CryptoCode) ? "BTC" : CryptoCode;
}
public string GetAddress()
{
if (Address == null)
return null;
var index = Address.IndexOf("#");
if (index == -1)
return Address;
return Address.Substring(0, index);
}
public HistoricalAddressInvoiceData SetAddress(string depositAddress, string cryptoCode)
{
Address = depositAddress + "#" + cryptoCode;
CryptoCode = cryptoCode;
return this;
}
#pragma warning restore CS0618
public DateTimeOffset Assigned

View file

@ -63,9 +63,9 @@ namespace BTCPayServer.HostedServices
}
CompositeDisposable leases = new CompositeDisposable();
async Task NotifyReceived(Script scriptPubKey)
async Task NotifyReceived(Script scriptPubKey, BTCPayNetwork network)
{
var invoice = await _InvoiceRepository.GetInvoiceIdFromScriptPubKey(scriptPubKey);
var invoice = await _InvoiceRepository.GetInvoiceIdFromScriptPubKey(scriptPubKey, network.CryptoCode);
if (invoice != null)
_WatchRequests.Add(invoice);
}
@ -149,7 +149,7 @@ namespace BTCPayServer.HostedServices
var getCoinsResponses = getCoinsResponsesAsync.Select(g => g.Result).ToArray();
foreach (var response in getCoinsResponses)
{
response.Coins = response.Coins.Where(c => invoice.AvailableAddressHashes.Contains(c.ScriptPubKey.Hash.ToString())).ToArray();
response.Coins = response.Coins.Where(c => invoice.AvailableAddressHashes.Contains(c.ScriptPubKey.Hash.ToString() + response.Strategy.Network.CryptoCode)).ToArray();
}
var coins = getCoinsResponses.Where(s => s.Coins.Length != 0).FirstOrDefault();
bool dirtyAddress = false;
@ -208,7 +208,6 @@ namespace BTCPayServer.HostedServices
if (totalPaid < accounting.TotalDue && invoice.Payments.Count != 0 && invoice.ExceptionStatus != "paidPartial")
{
Logs.PayServer.LogInformation("Paid to " + cryptoData.DepositAddress);
invoice.ExceptionStatus = "paidPartial";
context.MarkDirty();
if (dirtyAddress)
@ -379,7 +378,7 @@ namespace BTCPayServer.HostedServices
}, null, 0, (int)PollInterval.TotalMilliseconds);
leases.Add(_EventAggregator.Subscribe<Events.NewBlockEvent>(async b => { await NotifyBlock(); }));
leases.Add(_EventAggregator.Subscribe<Events.TxOutReceivedEvent>(async b => { await NotifyReceived(b.ScriptPubKey); }));
leases.Add(_EventAggregator.Subscribe<Events.TxOutReceivedEvent>(async b => { await NotifyReceived(b.ScriptPubKey, b.Network); }));
leases.Add(_EventAggregator.Subscribe<Events.InvoiceCreatedEvent>(b => { Watch(b.InvoiceId); }));
return Task.CompletedTask;

View file

@ -15,7 +15,8 @@
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"BTCPAY_EXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32839/",
"BTCPAY_NETWORK": "regtest",
"ASPNETCORE_ENVIRONMENT": "Development",
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
@ -23,4 +24,4 @@
"applicationUrl": "http://localhost:14142/"
}
}
}
}

View file

@ -70,11 +70,11 @@ namespace BTCPayServer.Services.Invoices
}
}
public async Task<string> GetInvoiceIdFromScriptPubKey(Script scriptPubKey)
public async Task<string> GetInvoiceIdFromScriptPubKey(Script scriptPubKey, string cryptoCode)
{
using (var db = _ContextFactory.CreateContext())
{
var result = await db.AddressInvoices.FindAsync(scriptPubKey.Hash.ToString());
var result = await db.AddressInvoices.FindAsync(scriptPubKey.Hash.ToString() + "#" + cryptoCode);
return result?.InvoiceDataId;
}
}
@ -130,19 +130,14 @@ namespace BTCPayServer.Services.Invoices
throw new InvalidOperationException("CryptoCode unsupported");
context.AddressInvoices.Add(new AddressInvoiceData()
{
Address = BitcoinAddress.Create(cryptoData.DepositAddress, network.NBitcoinNetwork).ScriptPubKey.Hash.ToString(),
InvoiceDataId = invoice.Id,
CreatedTime = DateTimeOffset.UtcNow,
});
}.SetHash(BitcoinAddress.Create(cryptoData.DepositAddress, network.NBitcoinNetwork).ScriptPubKey.Hash, network.CryptoCode));
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
{
InvoiceDataId = invoice.Id,
Address = cryptoData.DepositAddress,
#pragma warning disable CS0618
CryptoCode = cryptoData.CryptoCode,
#pragma warning restore CS0618
Assigned = DateTimeOffset.UtcNow
});
}.SetAddress(cryptoData.DepositAddress, cryptoData.CryptoCode));
textSearch.Add(cryptoData.DepositAddress);
textSearch.Add(cryptoData.Calculate().TotalDue.ToString());
}
@ -189,17 +184,18 @@ namespace BTCPayServer.Services.Invoices
{
invoiceEntity.DepositAddress = currencyData.DepositAddress;
}
#pragma warning disable CS0618
#pragma warning restore CS0618
invoiceEntity.SetCryptoData(cryptoData);
invoice.Blob = ToBytes(invoiceEntity);
context.AddressInvoices.Add(new AddressInvoiceData() { Address = bitcoinAddress.ScriptPubKey.Hash.ToString(), InvoiceDataId = invoiceId, CreatedTime = DateTimeOffset.UtcNow });
context.AddressInvoices.Add(new AddressInvoiceData() {
InvoiceDataId = invoiceId, CreatedTime = DateTimeOffset.UtcNow }
.SetHash(bitcoinAddress.ScriptPubKey.Hash, network.CryptoCode));
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
{
InvoiceDataId = invoiceId,
Address = bitcoinAddress.ToString(),
Assigned = DateTimeOffset.UtcNow
});
}.SetAddress(bitcoinAddress.ToString(), network.CryptoCode));
await context.SaveChangesAsync();
AddToTextSearch(invoice.Id, bitcoinAddress.ToString());
@ -215,7 +211,7 @@ namespace BTCPayServer.Services.Invoices
continue;
var historical = new HistoricalAddressInvoiceData();
historical.InvoiceDataId = invoiceId;
historical.Address = address.Value.DepositAddress;
historical.SetAddress(address.Value.DepositAddress, cryptoCode);
historical.UnAssigned = DateTimeOffset.UtcNow;
context.Attach(historical);
context.Entry(historical).Property(o => o.UnAssigned).IsModified = true;
@ -329,13 +325,12 @@ namespace BTCPayServer.Services.Invoices
}
if (invoice.AddressInvoices != null)
{
entity.AvailableAddressHashes = invoice.AddressInvoices.Select(a => a.Address).ToHashSet();
entity.AvailableAddressHashes = invoice.AddressInvoices.Select(a => a.GetHash() + a.GetCryptoCode()).ToHashSet();
}
return entity;
}
public async Task<InvoiceEntity[]> GetInvoices(InvoiceQuery queryObject)
{
using (var context = _ContextFactory.CreateContext())
@ -431,7 +426,9 @@ namespace BTCPayServer.Services.Invoices
PaymentEntity entity = new PaymentEntity
{
Outpoint = receivedCoin.Outpoint,
#pragma warning disable CS0618
Output = receivedCoin.TxOut,
#pragma warning restore CS0618
ReceivedTime = DateTime.UtcNow
};