Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
212a816598 | |||
1fd9cb5e2a | |||
6d3ea65e03 | |||
7d8c3c1c81 | |||
47ddbff817 | |||
783132a012 | |||
f456d62d3c | |||
8b4e572e16 | |||
51ef3ec656 | |||
35a3618a42 | |||
aa59725176 | |||
a544cfd4f0 | |||
61b37f87dc | |||
42b3b9eec0 | |||
0c4a9a2e70 |
@ -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
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 + "/");
|
||||
|
12
BTCPayServer.Tests/Dockerfile
Normal file
12
BTCPayServer.Tests/Dockerfile
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
32
BTCPayServer.Tests/README.md
Normal file
32
BTCPayServer.Tests/README.md
Normal file
@ -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();
|
||||
}
|
||||
|
||||
public TestAccount CreateAccount()
|
||||
private string GetEnvironment(string variable, string defaultValue)
|
||||
{
|
||||
var var = Environment.GetEnvironmentVariable(variable);
|
||||
return String.IsNullOrEmpty(var) ? defaultValue : var;
|
||||
}
|
||||
|
||||
public TestAccount NewAccount()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,30 +26,53 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
GrantAccessAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
RegisterAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public BitcoinExtKey ExtKey
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public async Task GrantAccessAsync()
|
||||
{
|
||||
var extKey = new ExtKey().GetWif(parent.Network);
|
||||
await RegisterAsync();
|
||||
var store = await CreateStoreAsync();
|
||||
var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant);
|
||||
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
||||
await store.Pair(pairingCode.ToString(), StoreId);
|
||||
}
|
||||
public StoresController CreateStore()
|
||||
{
|
||||
return CreateStoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
public async Task<StoresController> CreateStoreAsync()
|
||||
{
|
||||
ExtKey = new ExtKey().GetWif(parent.Network);
|
||||
var store = parent.PayTester.GetController<StoresController>(UserId);
|
||||
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
||||
StoreId = store.CreatedStoreId;
|
||||
await store.UpdateStore(StoreId, new StoreViewModel()
|
||||
{
|
||||
DerivationScheme = ExtKey.Neuter().ToString() + "-[legacy]",
|
||||
SpeedPolicy = SpeedPolicy.MediumSpeed
|
||||
}, "Save");
|
||||
return store;
|
||||
}
|
||||
|
||||
private async Task RegisterAsync()
|
||||
{
|
||||
var account = parent.PayTester.GetController<AccountController>();
|
||||
await account.Register(new RegisterViewModel()
|
||||
{
|
||||
Email = "Bob@toto.com",
|
||||
Email = Guid.NewGuid() + "@toto.com",
|
||||
ConfirmPassword = "Kitten0@",
|
||||
Password = "Kitten0@",
|
||||
});
|
||||
UserId = account.RegisteredUserId;
|
||||
|
||||
var store = parent.PayTester.GetController<StoresController>(account.RegisteredUserId);
|
||||
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
||||
StoreId = store.CreatedStoreId;
|
||||
|
||||
await store.UpdateStore(StoreId, new StoreViewModel()
|
||||
{
|
||||
ExtPubKey = extKey.Neuter().ToString(),
|
||||
SpeedPolicy = SpeedPolicy.MediumSpeed
|
||||
});
|
||||
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
||||
await store.Pair(pairingCode.ToString(), StoreId);
|
||||
}
|
||||
|
||||
public Bitpay BitPay
|
||||
|
@ -13,6 +13,8 @@ using BTCPayServer.Servcices.Invoices;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using BTCPayServer.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@ -63,7 +65,7 @@ namespace BTCPayServer.Tests
|
||||
using(var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var user = tester.CreateAccount();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
@ -85,7 +87,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);
|
||||
|
||||
@ -104,6 +106,31 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanUseServerInitiatedPairingCode()
|
||||
{
|
||||
using(var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var acc = tester.NewAccount();
|
||||
acc.Register();
|
||||
acc.CreateStore();
|
||||
|
||||
var controller = tester.PayTester.GetController<StoresController>(acc.UserId);
|
||||
var token = (RedirectToActionResult)controller.CreateToken(acc.StoreId, new Models.StoreViewModels.CreateTokenViewModel()
|
||||
{
|
||||
Facade = Facade.Merchant.ToString(),
|
||||
Label = "bla",
|
||||
PublicKey = null
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
var pairingCode = (string)token.RouteValues["pairingCode"];
|
||||
|
||||
acc.BitPay.AuthorizeClient(new PairingCode(pairingCode)).GetAwaiter().GetResult();
|
||||
Assert.True(acc.BitPay.TestAccess(Facade.Merchant));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSendIPN()
|
||||
{
|
||||
@ -112,7 +139,7 @@ namespace BTCPayServer.Tests
|
||||
using(var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var acc = tester.CreateAccount();
|
||||
var acc = tester.NewAccount();
|
||||
acc.GrantAccess();
|
||||
var invoice = acc.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
@ -125,7 +152,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();
|
||||
@ -143,7 +170,7 @@ namespace BTCPayServer.Tests
|
||||
using(var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var user = tester.CreateAccount();
|
||||
var user = tester.NewAccount();
|
||||
Assert.False(user.BitPay.TestAccess(Facade.Merchant));
|
||||
user.GrantAccess();
|
||||
Assert.True(user.BitPay.TestAccess(Facade.Merchant));
|
||||
@ -193,7 +220,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);
|
||||
|
||||
|
50
BTCPayServer.Tests/docker-compose.yml
Normal file
50
BTCPayServer.Tests/docker-compose.yml
Normal file
@ -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
|
@ -8,7 +8,7 @@ namespace BTCPayServer.Authentication
|
||||
{
|
||||
public class BitTokenEntity
|
||||
{
|
||||
public string Name
|
||||
public string Facade
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -16,15 +16,7 @@ namespace BTCPayServer.Authentication
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public DateTimeOffset DateCreated
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool Active
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string PairedId
|
||||
public string StoreId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -46,11 +38,9 @@ namespace BTCPayServer.Authentication
|
||||
{
|
||||
return new BitTokenEntity()
|
||||
{
|
||||
Active = Active,
|
||||
DateCreated = DateCreated,
|
||||
Label = Label,
|
||||
Name = Name,
|
||||
PairedId = PairedId,
|
||||
Facade = Facade,
|
||||
StoreId = StoreId,
|
||||
PairingTime = PairingTime,
|
||||
SIN = SIN,
|
||||
Value = Value
|
||||
|
@ -26,17 +26,17 @@ namespace BTCPayServer.Authentication
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public DateTimeOffset PairingTime
|
||||
public DateTimeOffset CreatedTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public DateTimeOffset PairingExpiration
|
||||
public DateTimeOffset Expiration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string Token
|
||||
public string TokenValue
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@ -44,7 +44,7 @@ namespace BTCPayServer.Authentication
|
||||
|
||||
public bool IsExpired()
|
||||
{
|
||||
return DateTimeOffset.UtcNow > PairingExpiration;
|
||||
return DateTimeOffset.UtcNow > Expiration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using DBreeze;
|
||||
using BTCPayServer.Data;
|
||||
using DBreeze;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json;
|
||||
@ -6,175 +7,177 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer.Authentication
|
||||
{
|
||||
public class TokenRepository
|
||||
{
|
||||
public TokenRepository(DBreezeEngine engine)
|
||||
ApplicationDbContextFactory _Factory;
|
||||
public TokenRepository(ApplicationDbContextFactory dbFactory)
|
||||
{
|
||||
_Engine = engine;
|
||||
if(dbFactory == null)
|
||||
throw new ArgumentNullException(nameof(dbFactory));
|
||||
_Factory = dbFactory;
|
||||
}
|
||||
|
||||
|
||||
private readonly DBreezeEngine _Engine;
|
||||
public DBreezeEngine Engine
|
||||
public async Task<BitTokenEntity[]> GetTokens(string sin)
|
||||
{
|
||||
get
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return _Engine;
|
||||
return (await ctx.PairedSINData
|
||||
.Where(p => p.SIN == sin)
|
||||
.ToListAsync())
|
||||
.Select(p => CreateTokenEntity(p))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<BitTokenEntity[]> GetTokens(string sin)
|
||||
private BitTokenEntity CreateTokenEntity(PairedSINData data)
|
||||
{
|
||||
List<BitTokenEntity> tokens = new List<BitTokenEntity>();
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
return new BitTokenEntity()
|
||||
{
|
||||
tx.ValuesLazyLoadingIsOn = false;
|
||||
foreach(var row in tx.SelectForward<string, byte[]>($"T_{sin}"))
|
||||
{
|
||||
var token = ToObject<BitTokenEntity>(row.Value);
|
||||
tokens.Add(token);
|
||||
}
|
||||
}
|
||||
return Task.FromResult(tokens.ToArray());
|
||||
}
|
||||
|
||||
public Task<BitTokenEntity> CreateToken(string sin, string tokenName)
|
||||
{
|
||||
var token = new BitTokenEntity
|
||||
{
|
||||
Name = tokenName,
|
||||
Value = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32)),
|
||||
DateCreated = DateTimeOffset.UtcNow
|
||||
Label = data.Label,
|
||||
Facade = data.Facade,
|
||||
Value = data.Id,
|
||||
SIN = data.SIN,
|
||||
PairingTime = data.PairingTime,
|
||||
StoreId = data.StoreDataId
|
||||
};
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
{
|
||||
tx.Insert<string, byte[]>($"T_{sin}", token.Name, ToBytes(token));
|
||||
tx.Commit();
|
||||
}
|
||||
return Task.FromResult(token);
|
||||
}
|
||||
|
||||
public Task<bool> PairWithAsync(string pairingCode, string pairedId)
|
||||
public async Task<string> CreatePairingCodeAsync()
|
||||
{
|
||||
if(pairedId == null)
|
||||
throw new ArgumentNullException(nameof(pairedId));
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
string pairingCodeId = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6));
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var row = tx.Select<string, byte[]>("PairingCodes", pairingCode);
|
||||
if(row == null || !row.Exists)
|
||||
return Task.FromResult(false);
|
||||
tx.RemoveKey<string>("PairingCodes", pairingCode);
|
||||
try
|
||||
var now = DateTime.UtcNow;
|
||||
var expiration = DateTime.UtcNow + TimeSpan.FromMinutes(15);
|
||||
await ctx.PairingCodes.AddAsync(new PairingCodeData()
|
||||
{
|
||||
var pairingEntity = ToObject<PairingCodeEntity>(row.Value);
|
||||
if(pairingEntity.IsExpired())
|
||||
return Task.FromResult(false);
|
||||
row = tx.Select<string, byte[]>($"T_{pairingEntity.SIN}", pairingEntity.Facade);
|
||||
if(row == null || !row.Exists)
|
||||
return Task.FromResult(false);
|
||||
var token = ToObject<BitTokenEntity>(row.Value);
|
||||
if(token.Active)
|
||||
return Task.FromResult(false);
|
||||
token.Active = true;
|
||||
token.PairedId = pairedId;
|
||||
token.SIN = pairingEntity.SIN;
|
||||
token.Label = pairingEntity.Label;
|
||||
token.PairingTime = DateTimeOffset.UtcNow;
|
||||
tx.Insert($"TbP_{pairedId}", token.Value, ToBytes(token));
|
||||
tx.Insert($"T_{pairingEntity.SIN}", pairingEntity.Facade, ToBytes(token));
|
||||
}
|
||||
finally
|
||||
{
|
||||
tx.Commit();
|
||||
}
|
||||
Id = pairingCodeId,
|
||||
DateCreated = now,
|
||||
Expiration = expiration,
|
||||
TokenValue = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32))
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
return Task.FromResult(true);
|
||||
return pairingCodeId;
|
||||
}
|
||||
|
||||
public Task<BitTokenEntity[]> GetTokensByPairedIdAsync(string pairedId)
|
||||
public async Task<PairingCodeEntity> UpdatePairingCode(PairingCodeEntity pairingCodeEntity)
|
||||
{
|
||||
List<BitTokenEntity> tokens = new List<BitTokenEntity>();
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
tx.ValuesLazyLoadingIsOn = false;
|
||||
foreach(var row in tx.SelectForward<string, byte[]>($"TbP_{pairedId}"))
|
||||
{
|
||||
tokens.Add(ToObject<BitTokenEntity>(row.Value));
|
||||
}
|
||||
}
|
||||
return Task.FromResult(tokens.ToArray());
|
||||
}
|
||||
|
||||
public Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
|
||||
{
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
{
|
||||
var row = tx.Select<string, byte[]>("PairingCodes", pairingCode);
|
||||
if(row == null || !row.Exists)
|
||||
return Task.FromResult<PairingCodeEntity>(null);
|
||||
var pairingEntity = ToObject<PairingCodeEntity>(row.Value);
|
||||
if(pairingEntity.IsExpired())
|
||||
return Task.FromResult<PairingCodeEntity>(null);
|
||||
return Task.FromResult(pairingEntity);
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeEntity.Id);
|
||||
pairingCode.Label = pairingCodeEntity.Label;
|
||||
pairingCode.Facade = pairingCodeEntity.Facade;
|
||||
await ctx.SaveChangesAsync();
|
||||
return CreatePairingCodeEntity(pairingCode);
|
||||
}
|
||||
}
|
||||
public Task<PairingCodeEntity> AddPairingCodeAsync(PairingCodeEntity pairingCodeEntity)
|
||||
|
||||
public async Task<bool> PairWithStoreAsync(string pairingCodeId, string storeId)
|
||||
{
|
||||
pairingCodeEntity = Clone(pairingCodeEntity);
|
||||
pairingCodeEntity.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6));
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
tx.Insert("PairingCodes", pairingCodeEntity.Id, ToBytes(pairingCodeEntity));
|
||||
tx.Commit();
|
||||
}
|
||||
return Task.FromResult(pairingCodeEntity);
|
||||
}
|
||||
|
||||
private byte[] ToBytes<T>(T obj)
|
||||
{
|
||||
return ZipUtils.Zip(JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
private T ToObject<T>(byte[] value)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(ZipUtils.Unzip(value));
|
||||
}
|
||||
|
||||
private T Clone<T>(T obj)
|
||||
{
|
||||
return ToObject<T>(ToBytes(obj));
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> DeleteToken(string sin, string tokenName, string storeId)
|
||||
{
|
||||
var token = await GetToken(sin, tokenName);
|
||||
if(token == null || (token.PairedId != null && token.PairedId != storeId))
|
||||
return false;
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
{
|
||||
tx.RemoveKey<string>($"T_{sin}", tokenName);
|
||||
if(token.PairedId != null)
|
||||
tx.RemoveKey<string>($"TbP_" + token.PairedId, token.Value);
|
||||
tx.Commit();
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return false;
|
||||
pairingCode.StoreDataId = storeId;
|
||||
await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task<BitTokenEntity> GetToken(string sin, string tokenName)
|
||||
public async Task<bool> PairWithSINAsync(string pairingCodeId, string sin)
|
||||
{
|
||||
using(var tx = _Engine.GetTransaction())
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
tx.ValuesLazyLoadingIsOn = true;
|
||||
var row = tx.Select<string, byte[]>($"T_{sin}", tokenName);
|
||||
if(row == null || !row.Exists)
|
||||
return Task.FromResult<BitTokenEntity>(null);
|
||||
var token = ToObject<BitTokenEntity>(row.Value);
|
||||
if(!token.Active)
|
||||
return Task.FromResult<BitTokenEntity>(null);
|
||||
return Task.FromResult(token);
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return false;
|
||||
pairingCode.SIN = sin;
|
||||
await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private async Task ActivateIfComplete(ApplicationDbContext ctx, PairingCodeData pairingCode)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(pairingCode.SIN) && !string.IsNullOrEmpty(pairingCode.StoreDataId))
|
||||
{
|
||||
ctx.PairingCodes.Remove(pairingCode);
|
||||
await ctx.PairedSINData.AddAsync(new PairedSINData()
|
||||
{
|
||||
Id = pairingCode.TokenValue,
|
||||
PairingTime = DateTime.UtcNow,
|
||||
Facade = pairingCode.Facade,
|
||||
Label = pairingCode.Label,
|
||||
StoreDataId = pairingCode.StoreDataId,
|
||||
SIN = pairingCode.SIN
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<BitTokenEntity[]> GetTokensByStoreIdAsync(string storeId)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return (await ctx.PairedSINData.Where(p => p.StoreDataId == storeId).ToListAsync())
|
||||
.Select(c => CreateTokenEntity(c))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return CreatePairingCodeEntity(await ctx.PairingCodes.FindAsync(pairingCode));
|
||||
}
|
||||
}
|
||||
|
||||
private PairingCodeEntity CreatePairingCodeEntity(PairingCodeData data)
|
||||
{
|
||||
return new PairingCodeEntity()
|
||||
{
|
||||
Facade = data.Facade,
|
||||
Id = data.Id,
|
||||
Label = data.Label,
|
||||
Expiration = data.Expiration,
|
||||
CreatedTime = data.DateCreated,
|
||||
TokenValue = data.TokenValue,
|
||||
SIN = data.SIN
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> DeleteToken(string tokenId)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
if(token == null)
|
||||
return false;
|
||||
ctx.PairedSINData.Remove(token);
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BitTokenEntity> GetToken(string tokenId)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
return CreateTokenEntity(token);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Version>1.0.0.1</Version>
|
||||
<Version>1.0.0.7</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Build\dockerfiles\**" />
|
||||
@ -22,9 +22,9 @@
|
||||
<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.38" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.9" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.10" />
|
||||
<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);
|
||||
@ -51,7 +51,6 @@ namespace BTCPayServer.Configuration
|
||||
}
|
||||
DBreezeEngine db = new DBreezeEngine(CreateDBPath(opts, "TokensDB"));
|
||||
_Resources.Add(db);
|
||||
TokenRepository = new TokenRepository(db);
|
||||
|
||||
db = new DBreezeEngine(CreateDBPath(opts, "InvoiceDB"));
|
||||
_Resources.Add(db);
|
||||
@ -70,11 +69,6 @@ namespace BTCPayServer.Configuration
|
||||
}
|
||||
DBFactory = dbContext;
|
||||
InvoiceRepository = new InvoiceRepository(dbContext, db, Network);
|
||||
|
||||
|
||||
db = new DBreezeEngine(CreateDBPath(opts, "AddressMapping"));
|
||||
_Resources.Add(db);
|
||||
Wallet = new BTCPayWallet(Explorer, db);
|
||||
}
|
||||
|
||||
private static string CreateDBPath(BTCPayServerOptions opts, string name)
|
||||
@ -104,20 +98,11 @@ namespace BTCPayServer.Configuration
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public TokenRepository TokenRepository
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public InvoiceRepository InvoiceRepository
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public BTCPayWallet Wallet
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public ApplicationDbContextFactory DBFactory
|
||||
{
|
||||
get;
|
||||
|
@ -21,7 +21,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("tokens")]
|
||||
public async Task<GetTokensResponse> GetTokens()
|
||||
public async Task<GetTokensResponse> Tokens()
|
||||
{
|
||||
var tokens = await _TokenRepository.GetTokens(this.GetBitIdentity().SIN);
|
||||
return new GetTokensResponse(tokens);
|
||||
@ -29,33 +29,51 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[Route("tokens")]
|
||||
public async Task<DataWrapper<List<PairingCodeResponse>>> GetPairingCode([FromBody] PairingCodeRequest token)
|
||||
public async Task<DataWrapper<List<PairingCodeResponse>>> Tokens([FromBody] TokenRequest request)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var pairingEntity = new PairingCodeEntity()
|
||||
PairingCodeEntity pairingEntity = null;
|
||||
if(string.IsNullOrEmpty(request.PairingCode))
|
||||
{
|
||||
Facade = token.Facade,
|
||||
Label = token.Label,
|
||||
SIN = token.Id,
|
||||
PairingTime = now,
|
||||
PairingExpiration = now + TimeSpan.FromMinutes(15)
|
||||
};
|
||||
var grantedToken = await _TokenRepository.CreateToken(token.Id, token.Facade);
|
||||
pairingEntity.Token = grantedToken.Name;
|
||||
pairingEntity = await _TokenRepository.AddPairingCodeAsync(pairingEntity);
|
||||
if(string.IsNullOrEmpty(request.Id) || !NBitpayClient.Extensions.BitIdExtensions.ValidateSIN(request.Id))
|
||||
throw new BitpayHttpException(400, "'id' property is required");
|
||||
if(string.IsNullOrEmpty(request.Facade))
|
||||
throw new BitpayHttpException(400, "'facade' property is required");
|
||||
|
||||
var pairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
||||
await _TokenRepository.PairWithSINAsync(pairingCode, request.Id);
|
||||
pairingEntity = await _TokenRepository.UpdatePairingCode(new PairingCodeEntity()
|
||||
{
|
||||
Id = pairingCode,
|
||||
Facade = request.Facade,
|
||||
Label = request.Label
|
||||
});
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var sin = this.GetBitIdentity(false)?.SIN ?? request.Id;
|
||||
if(string.IsNullOrEmpty(request.Id) || !NBitpayClient.Extensions.BitIdExtensions.ValidateSIN(request.Id))
|
||||
throw new BitpayHttpException(400, "'id' property is required, alternatively, use BitId");
|
||||
|
||||
pairingEntity = await _TokenRepository.GetPairingAsync(request.PairingCode);
|
||||
pairingEntity.SIN = sin;
|
||||
if(!await _TokenRepository.PairWithSINAsync(request.PairingCode, sin))
|
||||
throw new BitpayHttpException(400, "Unknown pairing code");
|
||||
|
||||
}
|
||||
|
||||
var pairingCodes = new List<PairingCodeResponse>
|
||||
{
|
||||
new PairingCodeResponse()
|
||||
{
|
||||
PairingCode = pairingEntity.Id,
|
||||
PairingExpiration = pairingEntity.PairingExpiration,
|
||||
DateCreated = pairingEntity.PairingTime,
|
||||
Facade = grantedToken.Name,
|
||||
Token = grantedToken.Value,
|
||||
Label = pairingEntity.Label
|
||||
}
|
||||
};
|
||||
new PairingCodeResponse()
|
||||
{
|
||||
PairingCode = pairingEntity.Id,
|
||||
PairingExpiration = pairingEntity.Expiration,
|
||||
DateCreated = pairingEntity.CreatedTime,
|
||||
Facade = pairingEntity.Facade,
|
||||
Token = pairingEntity.TokenValue,
|
||||
Label = pairingEntity.Label
|
||||
}
|
||||
};
|
||||
return DataWrapper.Create(pairingCodes);
|
||||
}
|
||||
}
|
||||
|
@ -83,26 +83,26 @@ namespace BTCPayServer.Controllers
|
||||
if(facade == null)
|
||||
throw new ArgumentNullException(nameof(facade));
|
||||
|
||||
var actualTokens = (await _TokenRepository.GetTokens(this.GetBitIdentity().SIN)).Where(t => t.Active).ToArray();
|
||||
var actualTokens = (await _TokenRepository.GetTokens(this.GetBitIdentity().SIN)).ToArray();
|
||||
actualTokens = actualTokens.SelectMany(t => GetCompatibleTokens(t)).ToArray();
|
||||
|
||||
var actualToken = actualTokens.FirstOrDefault(a => a.Value.Equals(expectedToken, StringComparison.Ordinal));
|
||||
if(expectedToken == null || actualToken == null)
|
||||
{
|
||||
Logs.PayServer.LogDebug($"No token found for facade {facade} for SIN {this.GetBitIdentity().SIN}");
|
||||
throw new BitpayHttpException(401, $"This endpoint does not support the `{actualTokens.Select(a => a.Name).Concat(new[] { "user" }).FirstOrDefault()}` facade");
|
||||
throw new BitpayHttpException(401, $"This endpoint does not support the `{actualTokens.Select(a => a.Facade).Concat(new[] { "user" }).FirstOrDefault()}` facade");
|
||||
}
|
||||
return actualToken;
|
||||
}
|
||||
|
||||
private IEnumerable<BitTokenEntity> GetCompatibleTokens(BitTokenEntity token)
|
||||
{
|
||||
if(token.Name == Facade.Merchant.ToString())
|
||||
if(token.Facade == Facade.Merchant.ToString())
|
||||
{
|
||||
yield return token.Clone(Facade.User);
|
||||
yield return token.Clone(Facade.PointOfSale);
|
||||
}
|
||||
if(token.Name == Facade.PointOfSale.ToString())
|
||||
if(token.Facade == Facade.PointOfSale.ToString())
|
||||
{
|
||||
yield return token.Clone(Facade.User);
|
||||
}
|
||||
@ -111,7 +111,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<StoreData> FindStore(BitTokenEntity bitToken)
|
||||
{
|
||||
var store = await _StoreRepository.FindStore(bitToken.PairedId);
|
||||
var store = await _StoreRepository.FindStore(bitToken.StoreId);
|
||||
if(store == null)
|
||||
throw new BitpayHttpException(401, "Unknown store");
|
||||
return store;
|
||||
|
@ -33,6 +33,8 @@ using BTCPayServer.Servcices.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Validations;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
@ -99,7 +101,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]
|
||||
@ -173,11 +197,11 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> ListTokens(string storeId)
|
||||
{
|
||||
var model = new TokensViewModel();
|
||||
var tokens = await _TokenRepository.GetTokensByPairedIdAsync(storeId);
|
||||
var tokens = await _TokenRepository.GetTokensByStoreIdAsync(storeId);
|
||||
model.StatusMessage = StatusMessage;
|
||||
model.Tokens = tokens.Select(t => new TokenViewModel()
|
||||
{
|
||||
Facade = t.Name,
|
||||
Facade = t.Facade,
|
||||
Label = t.Label,
|
||||
SIN = t.SIN,
|
||||
Id = t.Value
|
||||
@ -195,16 +219,34 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var pairingCode = await _TokenController.GetPairingCode(new PairingCodeRequest()
|
||||
var tokenRequest = new TokenRequest()
|
||||
{
|
||||
Facade = model.Facade,
|
||||
Label = model.Label,
|
||||
Id = NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey))
|
||||
});
|
||||
|
||||
Id = model.PublicKey == null ? null : NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey))
|
||||
};
|
||||
|
||||
string pairingCode = null;
|
||||
if(model.PublicKey == null)
|
||||
{
|
||||
tokenRequest.PairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
||||
await _TokenRepository.UpdatePairingCode(new PairingCodeEntity()
|
||||
{
|
||||
Id = tokenRequest.PairingCode,
|
||||
Facade = model.Facade,
|
||||
Label = model.Label,
|
||||
});
|
||||
await _TokenRepository.PairWithStoreAsync(tokenRequest.PairingCode, storeId);
|
||||
pairingCode = tokenRequest.PairingCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
pairingCode = ((DataWrapper<List<PairingCodeResponse>>)await _TokenController.Tokens(tokenRequest)).Data[0].PairingCode;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(RequestPairing), new
|
||||
{
|
||||
pairingCode = pairingCode.Data[0].PairingCode,
|
||||
pairingCode = pairingCode,
|
||||
selectedStore = storeId
|
||||
});
|
||||
}
|
||||
@ -215,22 +257,21 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var model = new CreateTokenViewModel();
|
||||
model.Facade = "merchant";
|
||||
if(_Env.IsDevelopment())
|
||||
{
|
||||
model.PublicKey = new Key().PubKey.ToHex();
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[Route("{storeId}/Tokens/Delete")]
|
||||
public async Task<IActionResult> DeleteToken(string storeId, string name, string sin)
|
||||
public async Task<IActionResult> DeleteToken(string storeId, string tokenId)
|
||||
{
|
||||
if(await _TokenRepository.DeleteToken(sin, name, storeId))
|
||||
StatusMessage = "Token revoked";
|
||||
else
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if(token == null ||
|
||||
token.StoreId != storeId ||
|
||||
!await _TokenRepository.DeleteToken(tokenId))
|
||||
StatusMessage = "Failure to revoke this token";
|
||||
else
|
||||
StatusMessage = "Token revoked";
|
||||
return RedirectToAction(nameof(ListTokens));
|
||||
}
|
||||
|
||||
@ -253,7 +294,7 @@ namespace BTCPayServer.Controllers
|
||||
Id = pairing.Id,
|
||||
Facade = pairing.Facade,
|
||||
Label = pairing.Label,
|
||||
SIN = pairing.SIN,
|
||||
SIN = pairing.SIN ?? "Server-Initiated Pairing",
|
||||
SelectedStore = selectedStore ?? stores.FirstOrDefault()?.Id,
|
||||
Stores = stores.Select(s => new PairingModel.StoreViewModel()
|
||||
{
|
||||
@ -270,11 +311,14 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> Pair(string pairingCode, string selectedStore)
|
||||
{
|
||||
var store = await _Repo.FindStore(selectedStore, GetUserId());
|
||||
if(store == null)
|
||||
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
|
||||
if(store == null || pairing == null)
|
||||
return NotFound();
|
||||
if(pairingCode != null && await _TokenRepository.PairWithAsync(pairingCode, store.Id))
|
||||
if(pairingCode != null && await _TokenRepository.PairWithStoreAsync(pairingCode, store.Id))
|
||||
{
|
||||
StatusMessage = "Pairing is successfull";
|
||||
if(pairing.SIN == null)
|
||||
StatusMessage = "Server initiated pairing code: " + pairingCode;
|
||||
return RedirectToAction(nameof(ListTokens), new
|
||||
{
|
||||
storeId = store.Id
|
||||
|
25
BTCPayServer/Data/AddressInvoiceData.cs
Normal file
25
BTCPayServer/Data/AddressInvoiceData.cs
Normal file
@ -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,11 +46,27 @@ namespace BTCPayServer.Data
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DbSet<AddressInvoiceData> AddressInvoices
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DbSet<SettingData> Settings
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public DbSet<PairingCodeData> PairingCodes
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DbSet<PairedSINData> PairedSINData
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var isConfigured = optionsBuilder.Options.Extensions.OfType<RelationalOptionsExtension>().Any();
|
||||
@ -86,6 +102,18 @@ namespace BTCPayServer.Data
|
||||
.HasOne(pt => pt.StoreData)
|
||||
.WithMany(t => t.UserStores)
|
||||
.HasForeignKey(pt => pt.StoreDataId);
|
||||
|
||||
builder.Entity<AddressInvoiceData>()
|
||||
.HasKey(o => o.Address);
|
||||
|
||||
builder.Entity<PairingCodeData>()
|
||||
.HasKey(o => o.Id);
|
||||
|
||||
builder.Entity<PairedSINData>(b =>
|
||||
{
|
||||
b.HasIndex(o => o.SIN);
|
||||
b.HasIndex(o => o.StoreDataId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
BTCPayServer/Data/PairedSINData.cs
Normal file
39
BTCPayServer/Data/PairedSINData.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class PairedSINData
|
||||
{
|
||||
public string Id
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Facade
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string StoreDataId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string Label
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public DateTimeOffset PairingTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string SIN
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
50
BTCPayServer/Data/PairingCodeData.cs
Normal file
50
BTCPayServer/Data/PairingCodeData.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class PairingCodeData
|
||||
{
|
||||
public string Id
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Facade
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string StoreDataId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public DateTimeOffset Expiration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string SIN
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public DateTime DateCreated
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string TokenValue
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
@ -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"]
|
@ -38,10 +38,10 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
|
||||
public static BitIdentity GetBitIdentity(this Controller controller)
|
||||
public static BitIdentity GetBitIdentity(this Controller controller, bool throws = true)
|
||||
{
|
||||
if(!(controller.User.Identity is BitIdentity))
|
||||
throw new UnauthorizedAccessException("no-bitid");
|
||||
return throws ? throw new UnauthorizedAccessException("no-bitid") : (BitIdentity)null;
|
||||
return (BitIdentity)controller.User.Identity;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ using Microsoft.AspNetCore.Identity;
|
||||
using BTCPayServer.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Authentication;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
@ -106,12 +108,12 @@ namespace BTCPayServer.Hosting
|
||||
runtime.Configure(o.GetRequiredService<BTCPayServerOptions>());
|
||||
return runtime;
|
||||
});
|
||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().TokenRepository);
|
||||
services.TryAddSingleton<TokenRepository>();
|
||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().InvoiceRepository);
|
||||
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()
|
||||
{
|
||||
|
@ -92,8 +92,9 @@ namespace BTCPayServer.Hosting
|
||||
private static async Task HandleBitpayHttpException(HttpContext httpContext, BitpayHttpException ex)
|
||||
{
|
||||
httpContext.Response.StatusCode = ex.StatusCode;
|
||||
using(var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, true))
|
||||
using(var writer = new StreamWriter(httpContext.Response.Body, new UTF8Encoding(false), 1024, true))
|
||||
{
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
var result = JsonConvert.SerializeObject(new BitpayErrorsModel(ex));
|
||||
writer.Write(result);
|
||||
await writer.FlushAsync();
|
||||
|
@ -61,15 +61,13 @@ namespace BTCPayServer.Hosting
|
||||
}
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Big hack, tests fails because Hangfire fail at initializing at the second test run
|
||||
|
||||
|
||||
services.ConfigureBTCPayServer(Configuration);
|
||||
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Big hack, tests fails because Hangfire fail at initializing at the second test run
|
||||
AddHangfireFix(services);
|
||||
services.AddBTCPayServer();
|
||||
services.AddMvc();
|
||||
|
392
BTCPayServer/Migrations/20171006013443_AddressMapping.Designer.cs
generated
Normal file
392
BTCPayServer/Migrations/20171006013443_AddressMapping.Designer.cs
generated
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
41
BTCPayServer/Migrations/20171006013443_AddressMapping.cs
Normal file
41
BTCPayServer/Migrations/20171006013443_AddressMapping.cs
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
444
BTCPayServer/Migrations/20171010082424_Tokens.Designer.cs
generated
Normal file
444
BTCPayServer/Migrations/20171010082424_Tokens.Designer.cs
generated
Normal file
@ -0,0 +1,444 @@
|
||||
// <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("20171010082424_Tokens")]
|
||||
partial class Tokens
|
||||
{
|
||||
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.PairedSINData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<DateTimeOffset>("PairingTime");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SIN");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("PairedSINData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("DateCreated");
|
||||
|
||||
b.Property<DateTimeOffset>("Expiration");
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.Property<string>("TokenValue");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PairingCodes");
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
67
BTCPayServer/Migrations/20171010082424_Tokens.cs
Normal file
67
BTCPayServer/Migrations/20171010082424_Tokens.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
public partial class Tokens : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PairedSINData",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PairingTime = table.Column<DateTimeOffset>(nullable: false),
|
||||
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PairedSINData", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PairingCodes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DateCreated = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTimeOffset>(nullable: false),
|
||||
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TokenValue = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PairingCodes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PairedSINData_SIN",
|
||||
table: "PairedSINData",
|
||||
column: "SIN");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PairedSINData_StoreDataId",
|
||||
table: "PairedSINData",
|
||||
column: "StoreDataId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PairedSINData");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PairingCodes");
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
@ -48,6 +62,58 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("Invoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<DateTimeOffset>("PairingTime");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SIN");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("PairedSINData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("DateCreated");
|
||||
|
||||
b.Property<DateTimeOffset>("Expiration");
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.Property<string>("TokenValue");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PairingCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -286,6 +352,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")
|
||||
|
@ -39,13 +39,13 @@ namespace BTCPayServer.Models
|
||||
{
|
||||
JObject item = new JObject();
|
||||
jarray.Add(item);
|
||||
JProperty jProp = new JProperty(token.Name);
|
||||
JProperty jProp = new JProperty(token.Facade);
|
||||
item.Add(jProp);
|
||||
jProp.Value = token.Value;
|
||||
}
|
||||
context.HttpContext.Response.Headers.Add("Content-Type", new Microsoft.Extensions.Primitives.StringValues("application/json"));
|
||||
var str = JsonConvert.SerializeObject(jobj);
|
||||
using(var writer = new StreamWriter(context.HttpContext.Response.Body, Encoding.UTF8, 1024 * 10, true))
|
||||
using(var writer = new StreamWriter(context.HttpContext.Response.Body, new UTF8Encoding(false), 1024 * 10, true))
|
||||
{
|
||||
await writer.WriteLineAsync(str);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Models
|
||||
{
|
||||
public class PairingCodeRequest
|
||||
public class TokenRequest
|
||||
{
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id
|
||||
@ -34,6 +34,12 @@ namespace BTCPayServer.Models
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "pairingCode")]
|
||||
public string PairingCode
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingCodeResponse
|
||||
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
BTCPayServer/Properties/launchSettings.json
Normal file
23
BTCPayServer/Properties/launchSettings.json
Normal file
@ -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)
|
@ -17,6 +17,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="PublicKey"></label>
|
||||
<small class="text-muted">Keep empty for server-initiated pairing</small>
|
||||
<input asp-for="PublicKey" class="form-control" />
|
||||
<span asp-validation-for="PublicKey" class="text-danger"></span>
|
||||
</div>
|
||||
|
@ -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>
|
||||
}
|
||||
|
@ -27,8 +27,7 @@
|
||||
<td>@token.Facade</td>
|
||||
<td>
|
||||
<form asp-action="DeleteToken" method="post">
|
||||
<input type="hidden" name="name" value="@token.Facade">
|
||||
<input type="hidden" name="sin" value="@token.SIN">
|
||||
<input type="hidden" name="tokenId" value="@token.Id">
|
||||
<button type="submit" class="btn btn-danger" role="button">Revoke</button>
|
||||
</form>
|
||||
</td>
|
||||
|
@ -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
|
Reference in New Issue
Block a user