Compare commits

..

11 Commits

36 changed files with 862 additions and 504 deletions

@ -1,6 +1,6 @@
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
[Bb]in/
[Oo]bj/
**/[Bb]in/
**/[Oo]bj/
node_modules/
dist/
@ -121,4 +121,3 @@ bower_components
output
.vs
BTCPayServer.Tests/

1
.gitignore vendored

@ -47,7 +47,6 @@ dlldata.c
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c

@ -8,7 +8,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170720-02" />
<PackageReference Include="NBitcoin.TestFramework" Version="1.4.4" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
@ -17,4 +16,13 @@
<ProjectReference Include="..\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup>
<ItemGroup>
<None Update=".dockerignore">
<DependentUpon>Dockerfile</DependentUpon>
</None>
<None Update="docker-compose.yml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.Tests;
using NBXplorer;
using NBXplorer.DerivationStrategy;
using System;
@ -59,6 +58,11 @@ namespace BTCPayServer.Tests
get; set;
}
public string Postgres
{
get; set;
}
IWebHost _Host;
public void Start()
{
@ -73,6 +77,8 @@ namespace BTCPayServer.Tests
config.AppendLine($"explorer.url={NBXplorerUri.AbsoluteUri}");
config.AppendLine($"explorer.cookiefile={CookieFile}");
config.AppendLine($"hdpubkey={HDPrivateKey.Neuter().ToString(Network.RegTest)}");
if(Postgres != null)
config.AppendLine($"postgres=" + Postgres);
File.WriteAllText(Path.Combine(_Directory, "settings.config"), config.ToString());
ServerUri = new Uri("http://127.0.0.1:" + port + "/");

@ -0,0 +1,12 @@
FROM microsoft/dotnet:2.0.0-sdk
WORKDIR /app
# caches restore result by copying csproj file separately
COPY BTCPayServer.Tests/BTCPayServer.Tests.csproj BTCPayServer.Tests/BTCPayServer.Tests.csproj
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
WORKDIR /app/BTCPayServer.Tests
RUN dotnet restore
# copies the rest of your code
COPY . ../.
ENTRYPOINT ["dotnet", "test"]

@ -1,112 +0,0 @@
using NBitcoin;
using NBitcoin.Tests;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Xunit;
namespace BTCPayServer.Tests
{
public class NBXplorerTester : IDisposable
{
private string _Directory;
public NBXplorerTester(string scope)
{
if(scope == null)
throw new ArgumentNullException(nameof(scope));
this._Directory = scope;
}
Process _Process;
public CoreNode Node
{
get; set;
}
public void Start()
{
ProcessLauncher launcher = new ProcessLauncher();
launcher.GoTo("Repositories", true);
if(!launcher.GoTo(new[] { "nxbplorer", "NBXplorer" }))
{
launcher.Run("git", "clone https://github.com/dgarage/NBXplorer nxbplorer");
Assert.True(launcher.GoTo(new[] { "nxbplorer", "NBXplorer" }), "Could not clone nxbplorer");
}
launcher.PushDirectory();
if(!launcher.GoTo(new[] { "bin", "Release", "netcoreapp2.0" }) || !launcher.Exists("NBXplorer.dll"))
{
launcher.PopDirectory();
launcher.Run("git", "pull");
launcher.Run("git", "checkout master");
try
{
//Need to do that or VS insist in adding this repo as submodules :/
Utils.DeleteDirectory(Path.GetFullPath(Path.Combine(launcher.CurrentDirectory, "..", ".git")));
}
catch { }
launcher.Run("dotnet", "build /p:Configuration=Release");
Assert.True(launcher.GoTo(new[] { "bin", "Release", "netcoreapp2.0" }), "Could not build NBXplorer");
launcher.AssertExists("NBXplorer.dll");
}
var port = Utils.FreeTcpPort();
var launcher2 = new ProcessLauncher();
launcher2.GoTo(_Directory, true);
launcher2.GoTo("nbxplorer-datadir", true);
StringBuilder config = new StringBuilder();
config.AppendLine($"regtest=1");
config.AppendLine($"port={port}");
config.AppendLine($"rpc.url={Node.RPCUri.AbsoluteUri}");
config.AppendLine($"rpc.auth={Node.AuthenticationString}");
config.AppendLine($"node.endpoint={Node.NodeEndpoint.Address}:{Node.NodeEndpoint.Port}");
File.WriteAllText(Path.Combine(launcher2.CurrentDirectory, "settings.config"), config.ToString());
_Process = launcher.Start("dotnet", $"NBXplorer.dll --datadir \"{launcher2.CurrentDirectory}\"");
ExplorerClient = new NBXplorer.ExplorerClient(Node.Network, new Uri($"http://127.0.0.1:{port}/"));
CookieFile = Path.Combine(launcher2.CurrentDirectory, ".cookie");
File.Create(CookieFile).Close(); //Will be wipedout when the client starts
ExplorerClient.SetCookieAuth(CookieFile);
try
{
var cancellationSource = new CancellationTokenSource(10000);
ExplorerClient.WaitServerStarted(cancellationSource.Token);
}
catch(OperationCanceledException)
{
Assert.False(_Process.HasExited, "NBXplorer failed to launch");
throw;
}
}
public NBXplorer.ExplorerClient ExplorerClient
{
get; set;
}
public string CookieFile
{
get;
set;
}
public static NBXplorerTester Create([CallerMemberNameAttribute] string scope = null)
{
return new NBXplorerTester(scope);
}
public void Dispose()
{
if(_Process != null && !_Process.HasExited)
_Process.Kill();
}
}
}

@ -0,0 +1,32 @@
# How to run the tests
The tests depends on having a proper environment running with Postgres, Bitcoind, NBxplorer configured.
You can however use the `docker-compose.yml` of this folder to get it running.
```
docker-compose up nbxplorer
```
You can run the tests while it is running through your favorite IDE, or with
```
dotnet test
```
Once you want to stop
```
docker-compose down
```
You can run the tests inside a container by running
```
docker-compose run --rm tests
```
The Bitcoin RPC server is exposed to the host, for example, you can send 0.23111090 BTC to mohu16LH66ptoWGEL1GtP6KHTBJYXMWhEf.
```
bitcoin-cli -regtest -rpcport=43782 -rpcuser=ceiwHEbqWI83 -rpcpassword=DwubwWsoo3 sendtoaddress "mohu16LH66ptoWGEL1GtP6KHTBJYXMWhEf" 0.23111090
```

@ -2,10 +2,12 @@
using BTCPayServer.Models.AccountViewModels;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBitcoin.Tests;
using NBitcoin.RPC;
using NBitpayClient;
using NBXplorer;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
@ -21,8 +23,6 @@ namespace BTCPayServer.Tests
}
string _Directory;
NodeBuilder _Builder;
public ServerTester(string scope)
{
_Directory = scope;
@ -34,42 +34,45 @@ namespace BTCPayServer.Tests
Utils.DeleteDirectory(_Directory);
if(!Directory.Exists(_Directory))
Directory.CreateDirectory(_Directory);
_Builder = NodeBuilder.Create(_Directory, "0.14.2");
ExplorerNode = _Builder.CreateNode(false);
ExplorerNode.WhiteBind = true;
ExplorerNode.Start();
ExplorerNode.CreateRPCClient().Generate(101);
ExplorerTester = NBXplorerTester.Create(Path.Combine(_Directory, "explorer"));
ExplorerTester.Node = ExplorerNode;
ExplorerTester.Start();
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_RPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), Network);
ExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_NBXPLORERURL", "http://127.0.0.1:32838/")));
PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay"))
{
NBXplorerUri = ExplorerTester.ExplorerClient.Address,
CookieFile = ExplorerTester.CookieFile
NBXplorerUri = ExplorerClient.Address,
Postgres = GetEnvironment("TESTS_POSTGRES", "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver")
};
PayTester.Start();
}
private string GetEnvironment(string variable, string defaultValue)
{
var var = Environment.GetEnvironmentVariable(variable);
return String.IsNullOrEmpty(var) ? defaultValue : var;
}
public TestAccount CreateAccount()
{
return new TestAccount(this);
}
public CoreNode ExplorerNode
public RPCClient ExplorerNode
{
get; set;
}
public ExplorerClient ExplorerClient
{
get; set;
}
public BTCPayServerTester PayTester
{
get; set;
}
public NBXplorerTester ExplorerTester
{
get; set;
}
public Network Network
{
get;
@ -80,10 +83,6 @@ namespace BTCPayServer.Tests
{
if(PayTester != null)
PayTester.Dispose();
if(ExplorerTester != null)
ExplorerTester.Dispose();
if(_Builder != null)
_Builder.Dispose();
}
}
}

@ -33,7 +33,7 @@ namespace BTCPayServer.Tests
var account = parent.PayTester.GetController<AccountController>();
await account.Register(new RegisterViewModel()
{
Email = "Bob@toto.com",
Email = Guid.NewGuid() + "@toto.com",
ConfirmPassword = "Kitten0@",
Password = "Kitten0@",
});
@ -45,9 +45,9 @@ namespace BTCPayServer.Tests
await store.UpdateStore(StoreId, new StoreViewModel()
{
ExtPubKey = extKey.Neuter().ToString(),
DerivationScheme = extKey.Neuter().ToString() + "-[legacy]",
SpeedPolicy = SpeedPolicy.MediumSpeed
});
}, "Save");
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
await store.Pair(pairingCode.ToString(), StoreId);
}

@ -85,7 +85,7 @@ namespace BTCPayServer.Tests
Transaction tx = new Transaction();
tx.Outputs.AddRange(request.Details.Outputs.Select(o => new TxOut(o.Amount, o.Script)));
var cashCow = tester.ExplorerNode.CreateRPCClient();
var cashCow = tester.ExplorerNode;
tx = cashCow.FundRawTransaction(tx).Transaction;
tx = cashCow.SignRawTransaction(tx);
@ -125,7 +125,7 @@ namespace BTCPayServer.Tests
FullNotifications = true
});
BitcoinUrlBuilder url = new BitcoinUrlBuilder(invoice.PaymentUrls.BIP21);
tester.ExplorerNode.CreateRPCClient().SendToAddress(url.Address, url.Amount);
tester.ExplorerNode.SendToAddress(url.Address, url.Amount);
callbackServer.ProcessNextRequest((ctx) =>
{
var ipn = new StreamReader(ctx.Request.Body).ReadToEnd();
@ -193,7 +193,7 @@ namespace BTCPayServer.Tests
var rate = user.BitPay.GetRates();
var cashCow = tester.ExplorerNode.CreateRPCClient();
var cashCow = tester.ExplorerNode;
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
cashCow.SendToAddress(invoiceAddress, firstPayment);

@ -0,0 +1,50 @@
version: "3"
services:
tests:
build:
context: ..
dockerfile: BTCPayServer.Tests/Dockerfile
environment:
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
TESTS_NBXPLORERURL: http://nbxplorer:32838/
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
links:
- nbxplorer
nbxplorer:
image: nicolasdorier/nbxplorer:1.0.0.16
ports:
- "32838:32838"
expose:
- "32838"
environment:
NBXPLORER_NETWORK: regtest
NBXPLORER_RPCURL: http://bitcoind:43782/
NBXPLORER_RPCUSER: ceiwHEbqWI83
NBXPLORER_RPCPASSWORD: DwubwWsoo3
NBXPLORER_NODEENDPOINT: bitcoind:39388
NBXPLORER_BIND: 0.0.0.0:32838
NBXPLORER_NOAUTH: 1
links:
- bitcoind
- postgres
bitcoind:
image: nicolasdorier/docker-bitcoin:0.15.0.1
ports:
- "43782:43782"
- "39388:39388"
environment:
BITCOIN_EXTRA_ARGS: "regtest=1\nrpcport=43782\nport=39388\nwhitelist=0.0.0.0/0"
BITCOIN_RPC_USER: ceiwHEbqWI83
BITCOIN_RPC_PASSWORD: DwubwWsoo3
expose:
- "43782"
- "39388"
postgres:
image: postgres:9.6.5
ports:
- "39372:5432"

@ -1,123 +0,0 @@
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
[Bb]in/
[Oo]bj/
node_modules/
dist/
# mstest test results
TestResults
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.log
*.vspscc
*.vssscc
.builds
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
packages
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
[Bb]in
[Oo]bj
sql
TestResults
[Tt]est[Rr]esult*
*.Cache
ClientBin
[Ss]tyle[Cc]op.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
src/Rapporteringsregisteret.Web/assets/less/*.css
MetricResults/
*.sln.ide/
_configs/
# vnext stuff
bower_components
output
.vs

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.0.0.1</Version>
<Version>1.0.0.5</Version>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Build\dockerfiles\**" />
@ -24,7 +24,7 @@
<PackageReference Include="NBitcoin" Version="4.0.0.38" />
<PackageReference Include="NBitpayClient" Version="1.0.0.9" />
<PackageReference Include="DBreeze" Version="1.87.0" />
<PackageReference Include="NBXplorer.Client" Version="1.0.0.12" />
<PackageReference Include="NBXplorer.Client" Version="1.0.0.16" />
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.2" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />

@ -38,7 +38,7 @@ namespace BTCPayServer.Configuration
if(!Explorer.SetCookieAuth(opts.CookieFile))
Explorer.SetNoAuth();
CancellationTokenSource cts = new CancellationTokenSource(10000);
CancellationTokenSource cts = new CancellationTokenSource(30000);
try
{
Logs.Configuration.LogInformation("Trying to connect to explorer " + Explorer.Address.AbsoluteUri);
@ -74,7 +74,6 @@ namespace BTCPayServer.Configuration
db = new DBreezeEngine(CreateDBPath(opts, "AddressMapping"));
_Resources.Add(db);
Wallet = new BTCPayWallet(Explorer, db);
}
private static string CreateDBPath(BTCPayServerOptions opts, string name)
@ -113,11 +112,6 @@ namespace BTCPayServer.Configuration
get;
set;
}
public BTCPayWallet Wallet
{
get;
set;
}
public ApplicationDbContextFactory DBFactory
{
get;

@ -99,7 +99,7 @@ namespace BTCPayServer.Controllers
entity.DepositAddress = await _Wallet.ReserveAddressAsync(derivationStrategy);
entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity);
await _Wallet.MapAsync(entity.DepositAddress, entity.Id);
await _Wallet.MapAsync(entity.DepositAddress.ScriptPubKey, entity.Id);
await _Watcher.WatchAsync(entity.Id);
var resp = entity.EntityToDTO();
return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBitpayClient;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Linq;
@ -28,6 +29,7 @@ namespace BTCPayServer.Controllers
UserManager<ApplicationUser> userManager,
AccessTokenController tokenController,
BTCPayWallet wallet,
Network network,
IHostingEnvironment env)
{
_Repo = repo;
@ -36,7 +38,9 @@ namespace BTCPayServer.Controllers
_TokenController = tokenController;
_Wallet = wallet;
_Env = env;
_Network = network;
}
Network _Network;
BTCPayWallet _Wallet;
AccessTokenController _TokenController;
StoreRepository _Repo;
@ -82,13 +86,17 @@ namespace BTCPayServer.Controllers
StoresViewModel result = new StoresViewModel();
result.StatusMessage = StatusMessage;
var stores = await _Repo.GetStoresByUserId(GetUserId());
foreach(var store in stores)
var balances = stores.Select(async s => string.IsNullOrEmpty(s.DerivationStrategy) ? Money.Zero : await _Wallet.GetBalance(s.DerivationStrategy)).ToArray();
for(int i = 0; i < stores.Length; i++)
{
var store = stores[i];
result.Stores.Add(new StoresViewModel.StoreViewModel()
{
Id = store.Id,
Name = store.StoreName,
WebSite = store.StoreWebsite
WebSite = store.StoreWebsite,
Balance = await balances[i]
});
}
return View(result);
@ -106,7 +114,7 @@ namespace BTCPayServer.Controllers
vm.StoreName = store.StoreName;
vm.StoreWebsite = store.StoreWebsite;
vm.SpeedPolicy = store.SpeedPolicy;
vm.ExtPubKey = store.DerivationStrategy;
vm.DerivationScheme = store.DerivationStrategy;
vm.StatusMessage = StatusMessage;
return View(vm);
}
@ -114,7 +122,7 @@ namespace BTCPayServer.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
[Route("{storeId}")]
public async Task<IActionResult> UpdateStore(string storeId, StoreViewModel model)
public async Task<IActionResult> UpdateStore(string storeId, StoreViewModel model, string command)
{
if(!ModelState.IsValid)
{
@ -124,48 +132,64 @@ namespace BTCPayServer.Controllers
if(store == null)
return NotFound();
bool needUpdate = false;
if(store.SpeedPolicy != model.SpeedPolicy)
if(command == "Save")
{
needUpdate = true;
store.SpeedPolicy = model.SpeedPolicy;
}
if(store.StoreName != model.StoreName)
{
needUpdate = true;
store.StoreName = model.StoreName;
}
if(store.StoreWebsite != model.StoreWebsite)
{
needUpdate = true;
store.StoreWebsite = model.StoreWebsite;
}
if(store.DerivationStrategy != model.ExtPubKey)
{
needUpdate = true;
try
bool needUpdate = false;
if(store.SpeedPolicy != model.SpeedPolicy)
{
await _Wallet.TrackAsync(model.ExtPubKey);
store.DerivationStrategy = model.ExtPubKey;
needUpdate = true;
store.SpeedPolicy = model.SpeedPolicy;
}
catch
if(store.StoreName != model.StoreName)
{
ModelState.AddModelError(nameof(model.ExtPubKey), "Invalid Derivation Scheme");
return View(model);
needUpdate = true;
store.StoreName = model.StoreName;
}
if(store.StoreWebsite != model.StoreWebsite)
{
needUpdate = true;
store.StoreWebsite = model.StoreWebsite;
}
}
if(needUpdate)
{
await _Repo.UpdateStore(store);
StatusMessage = "Store successfully updated";
}
if(store.DerivationStrategy != model.DerivationScheme)
{
needUpdate = true;
try
{
await _Wallet.TrackAsync(model.DerivationScheme);
store.DerivationStrategy = model.DerivationScheme;
}
catch
{
ModelState.AddModelError(nameof(model.DerivationScheme), "Invalid Derivation Scheme");
return View(model);
}
}
return RedirectToAction(nameof(UpdateStore), new
if(needUpdate)
{
await _Repo.UpdateStore(store);
StatusMessage = "Store successfully updated";
}
return RedirectToAction(nameof(UpdateStore), new
{
storeId = storeId
});
}
else
{
storeId = storeId
});
var facto = new DerivationStrategyFactory(_Network);
var scheme = facto.Parse(model.DerivationScheme);
var line = scheme.GetLineFor(DerivationFeature.Deposit);
for(int i = 0; i < 10; i++)
{
var address = line.Derive((uint)i);
model.AddressSamples.Add((line.Path.Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(_Network).ToString()));
}
return View(model);
}
}
[HttpGet]
@ -201,7 +225,7 @@ namespace BTCPayServer.Controllers
Label = model.Label,
Id = NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey))
});
return RedirectToAction(nameof(RequestPairing), new
{
pairingCode = pairingCode.Data[0].PairingCode,

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Data
{
public class AddressInvoiceData
{
public string Address
{
get; set;
}
public InvoiceData InvoiceData
{
get; set;
}
public string InvoiceDataId
{
get; set;
}
}
}

@ -46,6 +46,11 @@ namespace BTCPayServer.Data
get; set;
}
public DbSet<AddressInvoiceData> AddressInvoices
{
get; set;
}
public DbSet<SettingData> Settings
{
get; set;
@ -86,6 +91,9 @@ namespace BTCPayServer.Data
.HasOne(pt => pt.StoreData)
.WithMany(t => t.UserStores)
.HasForeignKey(pt => pt.StoreDataId);
builder.Entity<AddressInvoiceData>()
.HasKey(o => o.Address);
}
}
}

@ -1,18 +0,0 @@
FROM microsoft/aspnetcore-build AS builder
WORKDIR /source
# caches restore result by copying csproj file separately
COPY *.csproj .
RUN dotnet restore
# copies the rest of your code
COPY . .
RUN dotnet publish --output /app/ --configuration Release
FROM microsoft/aspnetcore:2.0.0
WORKDIR /app
RUN mkdir /datadir
ENV BTCPAY_DATADIR=/datadir
VOLUME /datadir
COPY --from=builder "/app" .
ENTRYPOINT ["dotnet", "BTCPayServer.dll"]

@ -31,6 +31,7 @@ using Microsoft.AspNetCore.Identity;
using BTCPayServer.Models;
using System.Threading.Tasks;
using System.Threading;
using BTCPayServer.Services.Wallets;
namespace BTCPayServer.Hosting
{
@ -111,7 +112,7 @@ namespace BTCPayServer.Hosting
services.TryAddSingleton<Network>(o => o.GetRequiredService<BTCPayServerOptions>().Network);
services.TryAddSingleton<ApplicationDbContextFactory>(o => o.GetRequiredService<BTCPayServerRuntime>().DBFactory);
services.TryAddSingleton<StoreRepository>();
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().Wallet);
services.TryAddSingleton<BTCPayWallet>();
services.TryAddSingleton<CurrencyNameTable>();
services.TryAddSingleton<IFeeProvider>(o => new NBXplorerFeeProvider()
{

@ -0,0 +1,392 @@
// <auto-generated />
using BTCPayServer.Data;
using BTCPayServer.Servcices.Invoices;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171006013443_AddressMapping")]
partial class AddressMapping
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.Property<string>("Address")
.ValueGeneratedOnAdd();
b.Property<string>("InvoiceDataId");
b.HasKey("Address");
b.HasIndex("InvoiceDataId");
b.ToTable("AddressInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<DateTimeOffset>("Created");
b.Property<string>("CustomerEmail");
b.Property<string>("ExceptionStatus");
b.Property<string>("ItemCode");
b.Property<string>("OrderId");
b.Property<string>("Status");
b.Property<string>("StoreDataId");
b.HasKey("Id");
b.HasIndex("StoreDataId");
b.ToTable("Invoices");
});
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<string>("InvoiceDataId");
b.HasKey("Id");
b.HasIndex("InvoiceDataId");
b.ToTable("Payments");
});
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<string>("InvoiceDataId");
b.HasKey("Id");
b.HasIndex("InvoiceDataId");
b.ToTable("RefundAddresses");
});
modelBuilder.Entity("BTCPayServer.Data.SettingData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("Settings");
});
modelBuilder.Entity("BTCPayServer.Data.StoreData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("DerivationStrategy");
b.Property<int>("SpeedPolicy");
b.Property<byte[]>("StoreCertificate");
b.Property<string>("StoreName");
b.Property<string>("StoreWebsite");
b.HasKey("Id");
b.ToTable("Stores");
});
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
{
b.Property<string>("ApplicationUserId");
b.Property<string>("StoreDataId");
b.Property<string>("Role");
b.HasKey("ApplicationUserId", "StoreDataId");
b.HasIndex("StoreDataId");
b.ToTable("UserStore");
});
modelBuilder.Entity("BTCPayServer.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<bool>("RequiresEmailConfirmation");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany()
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany()
.HasForeignKey("StoreDataId");
});
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("Payments")
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("RefundAddresses")
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
.WithMany("UserStores")
.HasForeignKey("ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("UserStores")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
public partial class AddressMapping : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AddressInvoices",
columns: table => new
{
Address = table.Column<string>(type: "TEXT", nullable: false),
InvoiceDataId = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AddressInvoices", x => x.Address);
table.ForeignKey(
name: "FK_AddressInvoices_Invoices_InvoiceDataId",
column: x => x.InvoiceDataId,
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_AddressInvoices_InvoiceDataId",
table: "AddressInvoices",
column: "InvoiceDataId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AddressInvoices");
}
}
}

@ -20,6 +20,20 @@ namespace BTCPayServer.Migrations
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.Property<string>("Address")
.ValueGeneratedOnAdd();
b.Property<string>("InvoiceDataId");
b.HasKey("Address");
b.HasIndex("InvoiceDataId");
b.ToTable("AddressInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.Property<string>("Id")
@ -286,6 +300,13 @@ namespace BTCPayServer.Migrations
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany()
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")

@ -27,8 +27,8 @@ namespace BTCPayServer.Models.StoreViewModels
set;
}
[ExtPubKeyValidator]
public string ExtPubKey
[DerivationStrategyValidator]
public string DerivationScheme
{
get; set;
}
@ -39,6 +39,11 @@ namespace BTCPayServer.Models.StoreViewModels
get; set;
}
public List<(string KeyPath, string Address)> AddressSamples
{
get; set;
} = new List<(string KeyPath, string Address)>();
public string StatusMessage
{
get; set;

@ -1,4 +1,5 @@
using System;
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -31,6 +32,10 @@ namespace BTCPayServer.Models.StoreViewModels
{
get; set;
}
public Money Balance
{
get; set;
}
}
}
}

@ -52,10 +52,6 @@ namespace BTCPayServer
.Build();
host.StartAsync().GetAwaiter().GetResult();
var urls = host.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
if(urls.Count != 0)
{
OpenBrowser(urls.Select(url => url.Replace("0.0.0.0", "127.0.0.1")).First());
}
foreach(var url in urls)
{
logger.LogInformation("Listening on " + url);
@ -79,29 +75,5 @@ namespace BTCPayServer
loggerProvider.Dispose();
}
}
public static void OpenBrowser(string url)
{
try
{
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}")); // Works ok on windows
}
else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Process.Start("xdg-open", url); // Works ok on linux
}
else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url); // Not tested
}
else
{
}
}
catch { }
}
}
}

@ -0,0 +1,23 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14139/",
"sslPort": 0
}
},
"profiles": {
"Docker-Regtest": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"BTCPAY_EXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_NETWORK": "regtest",
"ASPNETCORE_ENVIRONMENT": "Development",
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
},
"applicationUrl": "http://localhost:14142/"
}
}
}

@ -12,6 +12,7 @@ using System.Threading;
using Microsoft.Extensions.Hosting;
using System.Collections.Concurrent;
using Hangfire;
using BTCPayServer.Services.Wallets;
namespace BTCPayServer.Servcices.Invoices
{
@ -21,11 +22,14 @@ namespace BTCPayServer.Servcices.Invoices
ExplorerClient _ExplorerClient;
DerivationStrategyFactory _DerivationFactory;
InvoiceNotificationManager _NotificationManager;
BTCPayWallet _Wallet;
public InvoiceWatcher(ExplorerClient explorerClient,
InvoiceRepository invoiceRepository,
BTCPayWallet wallet,
InvoiceNotificationManager notificationManager)
{
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
_ExplorerClient = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient));
_DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network);
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
@ -93,10 +97,18 @@ namespace BTCPayServer.Servcices.Invoices
{
var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);
changes = await _ExplorerClient.SyncAsync(strategy, changes, false, _Cts.Token).ConfigureAwait(false);
var utxos = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
var invoiceIds = utxos.Select(u => _Wallet.GetInvoiceId(u.Output.ScriptPubKey)).ToArray();
utxos =
utxos
.Where((u,i) => invoiceIds[i].GetAwaiter().GetResult() == invoice.Id)
.ToArray();
shouldWait = false; //should not wait, Sync is blocking call
List<Coin> receivedCoins = new List<Coin>();
foreach(var received in changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs))
foreach(var received in utxos)
if(received.Output.ScriptPubKey == invoice.DepositAddress.ScriptPubKey)
receivedCoins.Add(new Coin(received.Outpoint, received.Output));

@ -1,5 +1,4 @@
using DBreeze;
using NBitcoin;
using NBitcoin;
using NBXplorer;
using NBXplorer.DerivationStrategy;
using System;
@ -7,56 +6,61 @@ using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Data;
namespace BTCPayServer.Services.Wallets
{
public class BTCPayWallet
{
public class BTCPayWallet
{
private ExplorerClient _Client;
private DBreezeEngine _Engine;
private Serializer _Serializer;
private DerivationStrategyFactory _DerivationStrategyFactory;
ApplicationDbContextFactory _DBFactory;
public BTCPayWallet(ExplorerClient client, DBreezeEngine dbreeze)
public BTCPayWallet(ExplorerClient client, ApplicationDbContextFactory factory)
{
if(client == null)
throw new ArgumentNullException(nameof(client));
if(dbreeze == null)
throw new ArgumentNullException(nameof(dbreeze));
if(factory == null)
throw new ArgumentNullException(nameof(factory));
_Client = client;
_Engine = dbreeze;
_DBFactory = factory;
_Serializer = new NBXplorer.Serializer(_Client.Network);
_DerivationStrategyFactory = new DerivationStrategyFactory(_Client.Network);
}
public async Task<BitcoinAddress> ReserveAddressAsync(string walletIdentifier)
{
var pathInfo = await _Client.GetUnusedAsync(_DerivationStrategyFactory.Parse(walletIdentifier), DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
using(var tx = _Engine.GetTransaction())
{
var pathInfoBytes = ToBytes(pathInfo);
tx.Insert(AddressToKeyInfo, pathInfo.Address.ToString(), pathInfoBytes);
tx.Commit();
}
return pathInfo.Address;
return pathInfo.ScriptPubKey.GetDestinationAddress(_DerivationStrategyFactory.Network);
}
public async Task TrackAsync(string walletIdentifier)
public Task TrackAsync(string walletIdentifier)
{
await _Client.SyncAsync(_DerivationStrategyFactory.Parse(walletIdentifier), null, null, true).ConfigureAwait(false);
return _Client.TrackAsync(_DerivationStrategyFactory.Parse(walletIdentifier));
}
const string AddressToId = "AtI";
const string AddressToKeyInfo = "AtK";
public Task MapAsync(BitcoinAddress address, string id)
public async Task<string> GetInvoiceId(Script scriptPubKey)
{
using(var tx = _Engine.GetTransaction())
using(var db = _DBFactory.CreateContext())
{
tx.Insert(AddressToId, address.ToString(), id);
tx.Commit();
var result = await db.AddressInvoices.FindAsync(scriptPubKey.Hash.ToString());
return result?.InvoiceDataId;
}
}
public async Task MapAsync(Script address, string invoiceId)
{
using(var db = _DBFactory.CreateContext())
{
db.AddressInvoices.Add(new AddressInvoiceData()
{
Address = address.Hash.ToString(),
InvoiceDataId = invoiceId
});
await db.SaveChangesAsync();
}
return Task.FromResult(true);
}
private byte[] ToBytes<T>(T obj)
@ -69,5 +73,13 @@ namespace BTCPayServer.Services.Wallets
var tasks = transactions.Select(t => _Client.BroadcastAsync(t)).ToArray();
return Task.WhenAll(tasks);
}
public async Task<Money> GetBalance(string derivationStrategy)
{
var result = await _Client.SyncAsync(_DerivationStrategyFactory.Parse(derivationStrategy), null, true);
return result.Confirmed.UTXOs.Select(u => u.Output.Value)
.Concat(result.Unconfirmed.UTXOs.Select(u => u.Output.Value))
.Sum();
}
}
}

@ -1,4 +1,5 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@ -6,7 +7,7 @@ using System.Text;
namespace BTCPayServer.Validations
{
public class ExtPubKeyValidatorAttribute : ValidationAttribute
public class DerivationStrategyValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
@ -19,7 +20,7 @@ namespace BTCPayServer.Validations
return new ValidationResult("No Network specified");
try
{
new BitcoinExtPubKey((string)value, network);
new DerivationStrategyFactory(network).Parse((string)value);
return ValidationResult.Success;
}
catch(Exception ex)

@ -27,6 +27,7 @@
<tr>
<th>Name</th>
<th>Website</th>
<th>Balance</th>
<th>Actions</th>
</tr>
</thead>
@ -40,6 +41,7 @@
{
<a href="@store.WebSite">@store.WebSite</a>
}</td>
<td>@store.Balance</td>
<td><a asp-action="UpdateStore" asp-route-storeId="@store.Id">Settings</a></td>
</tr>
}

@ -36,11 +36,77 @@
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ExtPubKey"></label>
<input asp-for="ExtPubKey" class="form-control" />
<span asp-validation-for="ExtPubKey" class="text-danger"></span>
<h5>Derivation Scheme</h5>
@if(Model.AddressSamples.Count == 0)
{
<span>The DerivationScheme represents the destination of the funds received by your invoice. It is generated by your wallet software. Please, verify that you are generating the right addresses by clicking on 'Check ExtPubKey'</span>
}
</div>
<button type="submit" class="btn btn-default">Save</button>
<div class="form-group">
<input asp-for="DerivationScheme" class="form-control" />
<span asp-validation-for="DerivationScheme" class="text-danger"></span>
</div>
<div class="form-group">
@if(Model.AddressSamples.Count == 0)
{
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Address type</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>P2WPKH</td>
<td>xpub</td>
</tr>
<tr>
<td>P2SH-P2WPKH</td>
<td>xpub-[p2sh]</td>
</tr>
<tr>
<td>P2SH</td>
<td>xpub-[legacy]</td>
</tr>
<tr>
<td>Multi-sig P2WSH</td>
<td>2-of-xpub1-xpub2</td>
</tr>
<tr>
<td>Multi-sig P2SH-P2WSH</td>
<td>2-of-xpub1-xpub2-[p2sh]</td>
</tr>
<tr>
<td>Multi-sig P2SH</td>
<td>2-of-xpub1-xpub2-[legacy]</td>
</tr>
</tbody>
</table>
}
else
{
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Key path</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@foreach(var sample in Model.AddressSamples)
{
<tr>
<td>@sample.KeyPath</td>
<td>@sample.Address</td>
</tr>
}
</tbody>
</table>
}
</div>
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
<button name="command" type="submit" class="btn btn-default" value="Check">Check ExtPubKey</button>
</form>
</div>
</div>

@ -1,48 +0,0 @@
version: "3"
services:
btcpayserver:
ports:
- 8080:49392
expose:
- "49392"
build:
context: .
dockerfile: DockerFile
environment:
BTCPAY_POSTGRES: "User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver"
BTCPAY_NETWORK: regtest
BTCPAY_EXPLORERURL: http://nbxplorer:32838/
BTCPAY_BIND: 0.0.0.0:49392
links:
- nbxplorer
- postgres
nbxplorer:
image: nicolasdorier/nbxplorer:1.0.0.12
expose:
- "32838"
environment:
NBXPLORER_NETWORK: regtest
NBXPLORER_RPCURL: http://bitcoind:43782/
NBXPLORER_RPCUSER: ceiwHEbqWI83
NBXPLORER_RPCPASSWORD: DwubwWsoo3
NBXPLORER_NODEENDPOINT: bitcoind:8332
NBXPLORER_BIND: 0.0.0.0:32838
NBXPLORER_NOAUTH: 1
links:
- bitcoind
bitcoind:
image: nicolasdorier/bitcoin:0.15.0.1
environment:
BITCOIN_EXTRA_ARGS: "regtest=1\nrpcport=43782\nport=8332"
BITCOIN_RPC_USER: ceiwHEbqWI83
BITCOIN_RPC_PASSWORD: DwubwWsoo3
expose:
- "43782"
- "8332"
postgres:
image: postgres:9.6.5

@ -1,2 +0,0 @@
docker-compose -f docker-compose.regtest.yml down
docker-compose -f docker-compose.regtest.yml up --force-recreate --build

@ -1,10 +1,10 @@
FROM microsoft/aspnetcore-build AS builder
WORKDIR /source
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer.csproj
# Cache some dependencies
RUN cd BTCPayServer && dotnet restore && cd ..
COPY . .
RUN cd BTCPayServer && dotnet publish --output /app/ --configuration Release
RUN dotnet restore
COPY BTCPayServer/. .
RUN dotnet publish --output /app/ --configuration Release
FROM microsoft/aspnetcore:2.0.0
WORKDIR /app

@ -1,48 +0,0 @@
version: "3"
services:
btcpayserver:
ports:
- 8080:49392
expose:
- "49392"
build:
context: .
dockerfile: DockerFile
environment:
BTCPAY_POSTGRES: "User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver"
BTCPAY_NETWORK: regtest
BTCPAY_EXPLORERURL: http://nbxplorer:32838/
BTCPAY_BIND: 0.0.0.0:49392
links:
- nbxplorer
- postgres
nbxplorer:
image: nicolasdorier/nbxplorer:1.0.0.12
expose:
- "32838"
environment:
NBXPLORER_NETWORK: regtest
NBXPLORER_RPCURL: http://bitcoind:43782/
NBXPLORER_RPCUSER: ceiwHEbqWI83
NBXPLORER_RPCPASSWORD: DwubwWsoo3
NBXPLORER_NODEENDPOINT: bitcoind:8332
NBXPLORER_BIND: 0.0.0.0:32838
NBXPLORER_NOAUTH: 1
links:
- bitcoind
bitcoind:
image: nicolasdorier/bitcoin:0.15.0.1
environment:
BITCOIN_EXTRA_ARGS: "regtest=1\nrpcport=43782\nport=8332"
BITCOIN_RPC_USER: ceiwHEbqWI83
BITCOIN_RPC_PASSWORD: DwubwWsoo3
expose:
- "43782"
- "8332"
postgres:
image: postgres:9.6.5