Commit 3a244ac5 authored by Jeremy Bokobza's avatar Jeremy Bokobza Committed by GitHub

Merge pull request #86 from dangershony/wallet-to-fullnode

Moving the wallet feature to the full node
parents 3435ae71 8036646f
...@@ -297,3 +297,4 @@ Thumbs.db ...@@ -297,3 +297,4 @@ Thumbs.db
project.lock.json project.lock.json
/Breeze/src/Breeze.Daemon/Wallets /Breeze/src/Breeze.Daemon/Wallets
/Breeze/src/Breeze.Daemon/Logs /Breeze/src/Breeze.Daemon/Logs
/Breeze.UI/.vscode/.BROWSE.VC.DB-wal
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26430.6 VisualStudioVersion = 15.0.26430.12
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807563C4-7259-434D-B604-A14C3DCF8E30}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807563C4-7259-434D-B604-A14C3DCF8E30}"
EndProject EndProject
...@@ -17,16 +17,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Api.Tests", "src\Bre ...@@ -17,16 +17,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Api.Tests", "src\Bre
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Wallet", "src\Breeze.Wallet\Breeze.Wallet.csproj", "{D16CD478-9D1E-4C69-91AD-43539E94A215}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Wallet", "src\Breeze.Wallet\Breeze.Wallet.csproj", "{D16CD478-9D1E-4C69-91AD-43539E94A215}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Daemon", "src\Breeze.Daemon\Breeze.Daemon.csproj", "{1B598E33-667F-496D-BC0D-88276E8E7632}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Breeze.TumbleBit.Client", "src\Breeze.TumbleBit.Client\Breeze.TumbleBit.Client.csproj", "{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Breeze.TumbleBit.Client", "src\Breeze.TumbleBit.Client\Breeze.TumbleBit.Client.csproj", "{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Breeze.Common", "src\Breeze.Common\Breeze.Common.csproj", "{C726817D-9E2F-4DDC-90A4-B6895CF5309B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TumbleBit", "TumbleBit", "{1B724678-2B73-483E-B981-3A6733C2194E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TumbleBit", "TumbleBit", "{1B724678-2B73-483E-B981-3A6733C2194E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NTumbleBit", "src\NTumbleBit\NTumbleBit.csproj", "{29E411B1-5687-43EE-A71B-6CCEC2289129}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NTumbleBit", "src\NTumbleBit\NTumbleBit.csproj", "{29E411B1-5687-43EE-A71B-6CCEC2289129}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin", "..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj", "{6702A093-B42C-44DA-86BB-CD8F87E9A665}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin.Common", "..\..\StratisBitcoinFullNode\Stratis.Bitcoin.Common\Stratis.Bitcoin.Common.csproj", "{4F9F7CF7-326C-4FC0-9EFB-209536A42030}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Daemon", "src\Breeze.Daemon\Breeze.Daemon.csproj", "{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -45,22 +47,26 @@ Global ...@@ -45,22 +47,26 @@ Global
{D16CD478-9D1E-4C69-91AD-43539E94A215}.Debug|Any CPU.Build.0 = Debug|Any CPU {D16CD478-9D1E-4C69-91AD-43539E94A215}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D16CD478-9D1E-4C69-91AD-43539E94A215}.Release|Any CPU.ActiveCfg = Release|Any CPU {D16CD478-9D1E-4C69-91AD-43539E94A215}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D16CD478-9D1E-4C69-91AD-43539E94A215}.Release|Any CPU.Build.0 = Release|Any CPU {D16CD478-9D1E-4C69-91AD-43539E94A215}.Release|Any CPU.Build.0 = Release|Any CPU
{1B598E33-667F-496D-BC0D-88276E8E7632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B598E33-667F-496D-BC0D-88276E8E7632}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B598E33-667F-496D-BC0D-88276E8E7632}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B598E33-667F-496D-BC0D-88276E8E7632}.Release|Any CPU.Build.0 = Release|Any CPU
{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU {2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Release|Any CPU.Build.0 = Release|Any CPU {2490DD1A-6C14-47F2-A9C6-56761A52E2D9}.Release|Any CPU.Build.0 = Release|Any CPU
{C726817D-9E2F-4DDC-90A4-B6895CF5309B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C726817D-9E2F-4DDC-90A4-B6895CF5309B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C726817D-9E2F-4DDC-90A4-B6895CF5309B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C726817D-9E2F-4DDC-90A4-B6895CF5309B}.Release|Any CPU.Build.0 = Release|Any CPU
{29E411B1-5687-43EE-A71B-6CCEC2289129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29E411B1-5687-43EE-A71B-6CCEC2289129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29E411B1-5687-43EE-A71B-6CCEC2289129}.Debug|Any CPU.Build.0 = Debug|Any CPU {29E411B1-5687-43EE-A71B-6CCEC2289129}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29E411B1-5687-43EE-A71B-6CCEC2289129}.Release|Any CPU.ActiveCfg = Release|Any CPU {29E411B1-5687-43EE-A71B-6CCEC2289129}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29E411B1-5687-43EE-A71B-6CCEC2289129}.Release|Any CPU.Build.0 = Release|Any CPU {29E411B1-5687-43EE-A71B-6CCEC2289129}.Release|Any CPU.Build.0 = Release|Any CPU
{6702A093-B42C-44DA-86BB-CD8F87E9A665}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6702A093-B42C-44DA-86BB-CD8F87E9A665}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6702A093-B42C-44DA-86BB-CD8F87E9A665}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6702A093-B42C-44DA-86BB-CD8F87E9A665}.Release|Any CPU.Build.0 = Release|Any CPU
{4F9F7CF7-326C-4FC0-9EFB-209536A42030}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F9F7CF7-326C-4FC0-9EFB-209536A42030}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F9F7CF7-326C-4FC0-9EFB-209536A42030}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F9F7CF7-326C-4FC0-9EFB-209536A42030}.Release|Any CPU.Build.0 = Release|Any CPU
{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
...@@ -69,9 +75,7 @@ Global ...@@ -69,9 +75,7 @@ Global
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4} = {807563C4-7259-434D-B604-A14C3DCF8E30} {E7B3E9EB-34E8-4B10-B296-4D5270E314A4} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{BD5174B4-DCE8-4594-9A16-B83E56767770} = {807563C4-7259-434D-B604-A14C3DCF8E30} {BD5174B4-DCE8-4594-9A16-B83E56767770} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{D16CD478-9D1E-4C69-91AD-43539E94A215} = {807563C4-7259-434D-B604-A14C3DCF8E30} {D16CD478-9D1E-4C69-91AD-43539E94A215} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{1B598E33-667F-496D-BC0D-88276E8E7632} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{2490DD1A-6C14-47F2-A9C6-56761A52E2D9} = {807563C4-7259-434D-B604-A14C3DCF8E30} {2490DD1A-6C14-47F2-A9C6-56761A52E2D9} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{C726817D-9E2F-4DDC-90A4-B6895CF5309B} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{29E411B1-5687-43EE-A71B-6CCEC2289129} = {1B724678-2B73-483E-B981-3A6733C2194E} {29E411B1-5687-43EE-A71B-6CCEC2289129} = {1B724678-2B73-483E-B981-3A6733C2194E}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" /> <ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" />
<ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" /> <ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" />
</ItemGroup> </ItemGroup>
......
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Xunit;
using Moq;
using Breeze.Wallet;
using Breeze.Wallet.Controllers;
using Breeze.Common.JsonErrors;
using Breeze.Wallet.Helpers;
using Breeze.Wallet.Models;
using NBitcoin;
using NBitcoin.Protocol;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Connection;
namespace Breeze.Api.Tests
{
public class ControllersTests
{
[Fact]
public void CreateWalletSuccessfullyReturnsMnemonic()
{
Mnemonic mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve);
var mockWalletCreate = new Mock<IWalletManager>();
mockWalletCreate.Setup(wallet => wallet.CreateWallet(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), null)).Returns(mnemonic);
var controller = new WalletController(mockWalletCreate.Object, new Mock<ITracker>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object);
// Act
var result = controller.Create(new WalletCreationRequest
{
Name = "myName",
FolderPath = "",
Password = "",
Network = ""
});
// Assert
mockWalletCreate.VerifyAll();
var viewResult = Assert.IsType<JsonResult>(result);
Assert.Equal(mnemonic.ToString(), viewResult.Value);
Assert.NotNull(result);
}
[Fact]
public void LoadWalletSuccessfullyReturnsWalletModel()
{
Wallet.Wallet wallet = new Wallet.Wallet
{
Name = "myWallet",
Network = WalletHelpers.GetNetwork("mainnet")
};
var mockWalletWrapper = new Mock<IWalletManager>();
mockWalletWrapper.Setup(w => w.RecoverWallet(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<DateTime>(), null)).Returns(wallet);
var controller = new WalletController(mockWalletWrapper.Object, new Mock<ITracker>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object);
// Act
var result = controller.Recover(new WalletRecoveryRequest
{
Name = "myWallet",
FolderPath = "",
Password = "",
Network = "MainNet",
Mnemonic = "mnemonic"
});
// Assert
mockWalletWrapper.VerifyAll();
var viewResult = Assert.IsType<OkResult>(result);
Assert.Equal(200, viewResult.StatusCode);
}
[Fact]
public void RecoverWalletSuccessfullyReturnsWalletModel()
{
Wallet.Wallet wallet = new Wallet.Wallet
{
Name = "myWallet",
Network = WalletHelpers.GetNetwork("mainnet")
};
var mockWalletWrapper = new Mock<IWalletManager>();
mockWalletWrapper.Setup(w => w.LoadWallet(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(wallet);
var controller = new WalletController(mockWalletWrapper.Object, new Mock<ITracker>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object);
// Act
var result = controller.Load(new WalletLoadRequest
{
Name = "myWallet",
FolderPath = "",
Password = ""
});
// Assert
mockWalletWrapper.VerifyAll();
var viewResult = Assert.IsType<OkResult>(result);
Assert.Equal(200, viewResult.StatusCode);
}
[Fact]
public void FileNotFoundExceptionandReturns404()
{
var mockWalletWrapper = new Mock<IWalletManager>();
mockWalletWrapper.Setup(wallet => wallet.LoadWallet(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Throws<FileNotFoundException>();
var controller = new WalletController(mockWalletWrapper.Object, new Mock<ITracker>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object);
// Act
var result = controller.Load(new WalletLoadRequest
{
Name = "myName",
FolderPath = "",
Password = ""
});
// Assert
mockWalletWrapper.VerifyAll();
var viewResult = Assert.IsType<ErrorResult>(result);
Assert.NotNull(viewResult);
Assert.Equal(404, viewResult.StatusCode);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Breeze.Wallet.Helpers;
using NBitcoin;
using Xunit;
namespace Breeze.Api.Tests
{
public class WalletHelpersTest
{
[Fact]
public void GetMainNetworkRetuirnsNetworkMain()
{
Network network = WalletHelpers.GetNetwork("main");
Assert.Equal(Network.Main, network);
}
[Fact]
public void GetMainNetNetworkRetuirnsNetworkMain()
{
Network network = WalletHelpers.GetNetwork("mainnet");
Assert.Equal(Network.Main, network);
}
[Fact]
public void GetTestNetworkRetuirnsNetworkTest()
{
Network network = WalletHelpers.GetNetwork("test");
Assert.Equal(Network.TestNet, network);
}
[Fact]
public void GetTestNetNetworkRetuirnsNetworkTest()
{
Network network = WalletHelpers.GetNetwork("testnet");
Assert.Equal(Network.TestNet, network);
}
[Fact]
public void GetNetworkIsCaseInsensitive()
{
Network testNetwork = WalletHelpers.GetNetwork("Test");
Assert.Equal(Network.TestNet, testNetwork);
Network mainNetwork = WalletHelpers.GetNetwork("MainNet");
Assert.Equal(Network.Main, mainNetwork);
}
[Fact]
public void WrongNetworkThrowsArgumentException()
{
var exception = Record.Exception(() => WalletHelpers.GetNetwork("myNetwork"));
Assert.NotNull(exception);
Assert.IsType<ArgumentException>(exception);
}
}
}
...@@ -29,10 +29,13 @@ ...@@ -29,10 +29,13 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
<PackageReference Include="Stratis.Bitcoin" Version="1.0.1.8-alpha" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" /> <PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
</Project> </Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
</Project>
\ No newline at end of file
using System.Collections.Generic;
using System.Net;
namespace Breeze.Common.JsonErrors
{
public static class ErrorHelpers
{
public static ErrorResult BuildErrorResponse(HttpStatusCode statusCode, string message, string description)
{
ErrorResponse errorResponse = new ErrorResponse
{
Errors = new List<ErrorModel>
{
new ErrorModel
{
Status = (int) statusCode,
Message = message,
Description = description
}
}
};
return new ErrorResult((int)statusCode, errorResponse);
}
}
}
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Breeze.Common.JsonErrors
{
public class ErrorResponse
{
[JsonProperty(PropertyName = "errors")]
public List<ErrorModel> Errors { get; set; }
}
public class ErrorModel
{
[JsonProperty(PropertyName = "status")]
public int Status { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Breeze.Common.JsonErrors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Breeze.Common.JsonErrors
{
public class ErrorResult : ObjectResult
{
public ErrorResult(int statusCode, ErrorResponse value) : base(value)
{
StatusCode = statusCode;
}
}
}
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" /> <ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" />
<ProjectReference Include="..\Breeze.TumbleBit.Client\Breeze.TumbleBit.Client.csproj" /> <ProjectReference Include="..\Breeze.TumbleBit.Client\Breeze.TumbleBit.Client.csproj" />
<ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" /> <ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" />
...@@ -21,7 +22,6 @@ ...@@ -21,7 +22,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
<PackageReference Include="Stratis.Bitcoin" Version="1.0.1.8-alpha" />
</ItemGroup> </ItemGroup>
</Project> </Project>
...@@ -9,7 +9,15 @@ using Stratis.Bitcoin.Builder; ...@@ -9,7 +9,15 @@ using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Logging; using Stratis.Bitcoin.Logging;
using Breeze.Wallet; using Breeze.Wallet;
using NBitcoin;
using NBitcoin.Protocol;
using Stratis.Bitcoin.BlockStore;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.MemoryPool;
using Stratis.Bitcoin.Miner;
using Stratis.Bitcoin.Notifications; using Stratis.Bitcoin.Notifications;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Wallet;
namespace Breeze.Daemon namespace Breeze.Daemon
{ {
...@@ -17,32 +25,125 @@ namespace Breeze.Daemon ...@@ -17,32 +25,125 @@ namespace Breeze.Daemon
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
// configure Full Node IFullNodeBuilder fullNodeBuilder = null;
Logs.Configure(new LoggerFactory().AddConsole(LogLevel.Trace, false));
NodeSettings nodeSettings = NodeSettings.FromArguments(args); if (args.Contains("stratis"))
{
var fullNodeBuilder = new FullNodeBuilder() // configure Full Node
.UseNodeSettings(nodeSettings) Logs.Configure(Logs.GetLoggerFactory(args));
.UseWallet() if (NodeSettings.PrintHelp(args, Network.StratisMain))
.UseBlockNotification() return;
.UseTransactionNotification()
.UseApi(); var network = args.Contains("-testnet") ? InitStratisTest() : Network.StratisMain;
var nodeSettings = NodeSettings.FromArguments(args, "stratis", network, ProtocolVersion.ALT_PROTOCOL_VERSION);
// add the tumbler's settings
var tumblerAddress = args.SingleOrDefault(arg => arg.StartsWith("-tumbler-uri=")); if (args.Contains("light"))
if (!string.IsNullOrEmpty(tumblerAddress)) {
{ fullNodeBuilder = new FullNodeBuilder()
tumblerAddress = tumblerAddress.Replace("-tumbler-uri=", string.Empty); .UseNodeSettings(nodeSettings)
fullNodeBuilder.UseTumbleBit(new Uri(tumblerAddress)); .UseLightWallet()
} .UseBlockNotification()
.UseTransactionNotification()
var node = (FullNode)fullNodeBuilder.Build(); .UseApi();
}
// start Full Node - this will also start the API else
node.Start(); {
Console.WriteLine("Press any key to stop"); fullNodeBuilder = new FullNodeBuilder()
Console.ReadLine(); .UseNodeSettings(nodeSettings)
node.Dispose(); .UseStratisConsensus()
.UseBlockStore()
.UseMempool()
.UseWallet()
.AddPowPosMining()
.UseApi();
}
}
else
{
NodeSettings nodeSettings = NodeSettings.FromArguments(args);
if (args.Contains("light"))
{
fullNodeBuilder = new FullNodeBuilder()
.UseNodeSettings(nodeSettings)
.UseLightWallet()
.UseBlockNotification()
.UseTransactionNotification()
.UseApi();
}
else
{
fullNodeBuilder = new FullNodeBuilder()
.UseNodeSettings(nodeSettings)
.UseConsensus()
.UseBlockStore()
.UseMempool()
.UseWallet()
.UseApi();
}
}
// add the tumbler's settings
var tumblerAddress = args.SingleOrDefault(arg => arg.StartsWith("-tumbler-uri="));
if (!string.IsNullOrEmpty(tumblerAddress))
{
tumblerAddress = tumblerAddress.Replace("-tumbler-uri=", string.Empty);
fullNodeBuilder.UseTumbleBit(new Uri(tumblerAddress));
}
var node = fullNodeBuilder.Build();
//start Full Node - this will also start the API
node.Run();
} }
}
private static Network InitStratisTest()
{
Block.BlockSignature = true;
Transaction.TimeStamp = true;
var consensus = Network.StratisMain.Consensus.Clone();
consensus.PowLimit = new Target(uint256.Parse("0000ffff00000000000000000000000000000000000000000000000000000000"));
// The message start string is designed to be unlikely to occur in normal data.
// The characters are rarely used upper ASCII, not valid as UTF-8, and produce
// a large 4-byte int at any alignment.
var pchMessageStart = new byte[4];
pchMessageStart[0] = 0x71;
pchMessageStart[1] = 0x31;
pchMessageStart[2] = 0x21;
pchMessageStart[3] = 0x11;
var magic = BitConverter.ToUInt32(pchMessageStart, 0); //0x5223570;
var genesis = Network.StratisMain.GetGenesis().Clone();
genesis.Header.Time = 1493909211;
genesis.Header.Nonce = 2433759;
genesis.Header.Bits = consensus.PowLimit;
consensus.HashGenesisBlock = genesis.GetHash();
Guard.Assert(consensus.HashGenesisBlock == uint256.Parse("0x00000e246d7b73b88c9ab55f2e5e94d9e22d471def3df5ea448f5576b1d156b9"));
var builder = new NetworkBuilder()
.SetName("StratisTest")
.SetConsensus(consensus)
.SetMagic(magic)
.SetGenesis(genesis)
.SetPort(26178)
.SetRPCPort(26174)
.SetBase58Bytes(Base58Type.PUBKEY_ADDRESS, new byte[] { (65) })
.SetBase58Bytes(Base58Type.SCRIPT_ADDRESS, new byte[] { (196) })
.SetBase58Bytes(Base58Type.SECRET_KEY, new byte[] { (65 + 128) })
.SetBase58Bytes(Base58Type.ENCRYPTED_SECRET_KEY_NO_EC, new byte[] { 0x01, 0x42 })
.SetBase58Bytes(Base58Type.ENCRYPTED_SECRET_KEY_EC, new byte[] { 0x01, 0x43 })
.SetBase58Bytes(Base58Type.EXT_PUBLIC_KEY, new byte[] { (0x04), (0x88), (0xB2), (0x1E) })
.SetBase58Bytes(Base58Type.EXT_SECRET_KEY, new byte[] { (0x04), (0x88), (0xAD), (0xE4) })
.AddDNSSeeds(new[]
{
new DNSSeedData("stratisplatform.com", "testnode1.stratisplatform.com"),
});
return builder.BuildAndRegister();
}
}
} }
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.1.3" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="1.0.1" /> <PackageReference Include="Serilog.Extensions.Logging.File" Version="1.0.1" />
<PackageReference Include="Stratis.Bitcoin" Version="1.0.1.8-alpha" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Breeze.Common\Breeze.Common.csproj" /> <ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin.Common\Stratis.Bitcoin.Common.csproj" />
<ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\NTumbleBit\NTumbleBit.csproj" /> <ProjectReference Include="..\NTumbleBit\NTumbleBit.csproj" />
</ItemGroup> </ItemGroup>
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Breeze.Common.JsonErrors;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Breeze.TumbleBit.Client; using Breeze.TumbleBit.Client;
using Stratis.Bitcoin.Common.JsonErrors;
namespace Breeze.TumbleBit.Controllers namespace Breeze.TumbleBit.Controllers
{ {
......
...@@ -20,11 +20,10 @@ ...@@ -20,11 +20,10 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="1.0.1" /> <PackageReference Include="Serilog.Extensions.Logging.File" Version="1.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" /> <PackageReference Include="System.ValueTuple" Version="4.3.1" />
<PackageReference Include="Stratis.Bitcoin" Version="1.0.1.8-alpha" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Breeze.Common\Breeze.Common.csproj" /> <ProjectReference Include="..\..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
namespace Breeze.Wallet
{
public static class ChainExtensions
{
/// <summary>
/// Determines whether the chain is downloaded and up to date.
/// </summary>
/// <param name="chain">The chain.</param>
public static bool IsDownloaded(this ConcurrentChain chain)
{
return chain.Tip.Header.BlockTime.ToUnixTimeSeconds() > (DateTimeOffset.Now.ToUnixTimeSeconds() - TimeSpan.FromHours(1).TotalSeconds);
}
/// <summary>
/// Gets the height of the first block created after this date.
/// </summary>
/// <param name="chain">The chain of blocks.</param>
/// <param name="date">The date.</param>
/// <returns>The height of the first block created after the date.</returns>
public static int GetHeightAtTime(this ConcurrentChain chain, DateTime date)
{
int blockSyncStart = 0;
int upperLimit = chain.Tip.Height;
int lowerLimit = 0;
bool found = false;
while (!found)
{
int check = lowerLimit + (upperLimit - lowerLimit) / 2;
if (chain.GetBlock(check).Header.BlockTime >= date)
{
upperLimit = check;
}
else if (chain.GetBlock(check).Header.BlockTime < date)
{
lowerLimit = check;
}
if (upperLimit - lowerLimit <= 1)
{
blockSyncStart = upperLimit;
found = true;
}
}
return blockSyncStart;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using Stratis.Bitcoin.Utilities;
namespace Breeze.Wallet.Helpers
{
/// <summary>
/// Contains a collection of helpers methods.
/// </summary>
public static class WalletHelpers
{
/// <summary>
/// Get the network on which to operate.
/// </summary>
/// <param name="network">The network</param>
/// <returns>A <see cref="Network"/> object.</returns>
public static Network GetNetwork(string network)
{
Guard.NotEmpty(network, nameof(network));
switch (network.ToLowerInvariant())
{
case "main":
case "mainnet":
return Network.Main;
case "test":
case "testnet":
return Network.TestNet;
default:
throw new ArgumentException($"Network '{network}' is not a valid network.");
}
}
}
}
using System;
using System.Threading.Tasks;
using NBitcoin;
namespace Breeze.Wallet
{
public interface ITracker
{
/// <summary>
/// Initializes the tracker.
/// </summary>
/// <returns></returns>
Task Initialize();
/// <summary>
/// Waits for the chain to download.
/// </summary>
/// <returns></returns>
Task WaitForChainDownloadAsync();
/// <summary>
/// Synchronize the wallet starting from the date passed as a parameter.
/// </summary>
/// <param name="date">The date from which to start the sync process.</param>
/// <returns>The height of the block sync will start from</returns>
void SyncFrom(DateTime date);
/// <summary>
/// Synchronize the wallet starting from the height passed as a parameter.
/// </summary>
/// <param name="height">The height from which to start the sync process.</param>
/// <returns>The height of the block sync will start from</returns>
void SyncFrom(int height);
}
}
using System;
using System.Collections.Generic;
using Breeze.Wallet.Models;
using NBitcoin;
namespace Breeze.Wallet
{
/// <summary>
/// Interface for a manager providing operations on wallets.
/// </summary>
public interface IWalletManager : IDisposable
{
/// <summary>
/// Creates a wallet and persist it as a file on the local system.
/// </summary>
/// <param name="password">The password used to encrypt sensitive info.</param>
/// <param name="folderPath">The folder where the wallet will be saved.</param>
/// <param name="name">The name of the wallet.</param>
/// <param name="network">The network this wallet is for.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <returns>A mnemonic defining the wallet's seed used to generate addresses.</returns>
Mnemonic CreateWallet(string password, string folderPath, string name, string network, string passphrase = null);
/// <summary>
/// Loads a wallet from a file.
/// </summary>
/// <param name="password">The user's password.</param>
/// <param name="folderPath">The folder where the wallet will be loaded.</param>
/// <param name="name">The name of the wallet.</param>
/// <returns>The wallet.</returns>
Wallet LoadWallet(string password, string folderPath, string name);
/// <summary>
/// Recovers a wallet.
/// </summary>
/// <param name="password">The user's password.</param>
/// <param name="folderPath">The folder where the wallet will be loaded.</param>
/// <param name="name">The name of the wallet.</param>
/// <param name="network">The network in which to creae this wallet</param>
/// <param name="mnemonic">The user's mnemonic for the wallet.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <param name="creationTime">The date and time this wallet was created.</param>
/// <returns>The recovered wallet.</returns>
Wallet RecoverWallet(string password, string folderPath, string name, string network, string mnemonic, DateTime creationTime, string passphrase = null);
/// <summary>
/// Deletes a wallet.
/// </summary>
/// <param name="walletFilePath">The location of the wallet file.</param>
void DeleteWallet(string walletFilePath);
/// <summary>
/// Gets an account that contains no transactions.
/// </summary>
/// <param name="walletName">The name of the wallet from which to get an account.</param>
/// <param name="coinType">The type of coin for which to get an account.</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
/// <returns>An unused account.</returns>
HdAccount GetUnusedAccount(string walletName, CoinType coinType, string password);
/// <summary>
/// Gets an account that contains no transactions.
/// </summary>
/// <param name="wallet">The wallet from which to get an account.</param>
/// <param name="coinType">The type of coin for which to get an account.</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
/// <returns>An unused account.</returns>
HdAccount GetUnusedAccount(Wallet wallet, CoinType coinType, string password);
/// <summary>
/// Creates a new account.
/// </summary>
/// <param name="wallet">The wallet in which this account will be created.</param>
/// <param name="coinType">The type of coin for which to create an account.</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
/// <returns>The new account.</returns>
HdAccount CreateNewAccount(Wallet wallet, CoinType coinType, string password);
/// <summary>
/// Gets an address that contains no transaction.
/// </summary>
/// <param name="walletName">The name of the wallet in which this address is contained.</param>
/// <param name="coinType">The type of coin for which to get the address.</param>
/// <param name="accountName">The name of the account in which this address is contained.</param>
/// <returns>An unused address or a newly created address, in Base58 format.</returns>
string GetUnusedAddress(string walletName, CoinType coinType, string accountName);
/// <summary>
/// Gets a collection of addresses containing transactions for this coin.
/// </summary>
/// <param name="walletName">The name of the wallet to get history from.</param>
/// <param name="coinType">Type of the coin.</param>
/// <returns></returns>
IEnumerable<HdAddress> GetHistoryByCoinType(string walletName, CoinType coinType);
/// <summary>
/// Gets a collection of addresses containing transactions for this coin.
/// </summary>
/// <param name="wallet">The wallet to get history from.</param>
/// <param name="coinType">Type of the coin.</param>
/// <returns></returns>
IEnumerable<HdAddress> GetHistoryByCoinType(Wallet wallet, CoinType coinType);
/// <summary>
/// Gets some general information about a wallet.
/// </summary>
/// <param name="walletName">The name of the wallet.</param>
/// <returns></returns>
Wallet GetWallet(string walletName);
/// <summary>
/// Gets a list of accounts filtered by coin type.
/// </summary>
/// <param name="walletName">The name of the wallet to look into.</param>
/// <param name="coinType">The type of coin to filter by.</param>
/// <returns></returns>
IEnumerable<HdAccount> GetAccountsByCoinType(string walletName, CoinType coinType);
/// <summary>
/// Builds a transaction to be sent to the network.
/// </summary>
/// <param name="walletName">The name of the wallet in which this address is contained.</param>
/// <param name="coinType">The type of coin for which to get the address.</param>
/// <param name="accountName">The name of the account in which this address is contained.</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <param name="destinationAddress">The destination address to send the funds to.</param>
/// <param name="amount">The amount of funds to be sent.</param>
/// <param name="feeType">The type of fee to be included.</param>
/// <param name="allowUnconfirmed">Whether or not we allow this transaction to rely on unconfirmed outputs.</param>
/// <returns></returns>
(string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, CoinType coinType, string password, string destinationAddress, Money amount, string feeType, bool allowUnconfirmed);
/// <summary>
/// Sends a transaction to the network.
/// </summary>
/// <param name="transactionHex">The hex of the transaction.</param>
/// <returns></returns>
bool SendTransaction(string transactionHex);
/// <summary>
/// Processes a block received from the network.
/// </summary>
/// <param name="height">The height of the block in the blockchain.</param>
/// <param name="block">The block.</param>
void ProcessBlock(int height, Block block);
/// <summary>
/// Processes a transaction received from the network.
/// </summary>
/// <param name="transaction">The transaction.</param>
/// <param name="blockHeight">The height of the block this transaction came from. Null if it was not a transaction included in a block.</param>
/// <param name="block">The block in which this transaction was included.</param>
void ProcessTransaction(Transaction transaction, int? blockHeight = null, Block block = null);
/// <summary>
/// Saves the wallet into the file system.
/// </summary>
/// <param name="wallet">The wallet to save.</param>
void SaveToFile(Wallet wallet);
/// <summary>
/// Saves all the loaded wallets into the file system.
/// </summary>
void SaveToFile();
/// <summary>
/// Updates the wallet with the height of the last block synced.
/// </summary>
/// <param name="wallet">The wallet to update.</param>
/// <param name="height">The height of the last block synced.</param>
void UpdateLastBlockSyncedHeight(Wallet wallet, int height);
/// <summary>
/// Updates all the loaded wallets with the height of the last block synced.
/// </summary>
/// <param name="height">The height of the last block synced.</param>
void UpdateLastBlockSyncedHeight(int height);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace Breeze.Wallet.JsonConverters
{
/// <summary>
/// Converter used to convert <see cref="byte"/> arrays to and from JSON.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
public class ByteArrayConverter : JsonConverter
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(byte[]);
}
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return Convert.FromBase64String((string)reader.Value);
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(Convert.ToBase64String((byte[])value));
}
}
}
using Newtonsoft.Json;
using System;
namespace Breeze.Wallet.JsonConverters
{
/// <summary>
/// Converter used to convert <see cref="DateTimeOffset"/> to and from Unix time.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
public class DateTimeOffsetConverter : JsonConverter
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTimeOffset);
}
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)reader.Value));
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((DateTimeOffset)value).ToUnixTimeSeconds().ToString());
}
}
}
using System;
using Breeze.Wallet.Helpers;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.JsonConverters
{
/// <summary>
/// Converter used to convert <see cref="Network"/> to and from JSON.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
public class NetworkConverter : JsonConverter
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Network);
}
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return WalletHelpers.GetNetwork((string)reader.Value);
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Network)value).ToString());
}
}
}
using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Builder.Feature;
using Breeze.Wallet.Controllers;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.Builder; using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Logging; using Stratis.Bitcoin.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Serilog; using Serilog;
using Stratis.Bitcoin.Wallet;
using Stratis.Bitcoin.Wallet.Controllers;
namespace Breeze.Wallet namespace Breeze.Wallet
{ {
public class WalletFeature : FullNodeFeature public class LightWalletFeature : FullNodeFeature
{ {
private readonly ITracker tracker; private readonly LightWalletSyncManager lightWalletSyncManager;
private readonly IWalletManager walletManager;
public WalletFeature(ITracker tracker, IWalletManager walletManager) public LightWalletFeature(LightWalletSyncManager lightWalletSyncManager)
{ {
this.tracker = tracker; this.lightWalletSyncManager = lightWalletSyncManager;
this.walletManager = walletManager; }
}
public override void Start() public override void Start()
{ {
this.tracker.Initialize(); this.lightWalletSyncManager.Initialize();
} }
public override void Stop() public override void Stop()
{ {
this.walletManager.Dispose();
base.Stop(); base.Stop();
} }
} }
public static class WalletFeatureExtension public static class LightWalletFeatureExtension
{ {
public static IFullNodeBuilder UseWallet(this IFullNodeBuilder fullNodeBuilder) public static IFullNodeBuilder UseLightWallet(this IFullNodeBuilder fullNodeBuilder)
{ {
fullNodeBuilder.ConfigureFeature(features => fullNodeBuilder.ConfigureFeature(features =>
{ {
features features
.AddFeature<WalletFeature>() .AddFeature<LightWalletFeature>()
.FeatureServices(services => .FeatureServices(services =>
{ {
var loggerFactory = Logs.LoggerFactory; services.AddSingleton<IWalletSyncManager, LightWalletSyncManager>();
loggerFactory.AddFile("Logs/Breeze-{Date}.json", isJson: true, minimumLevel:LogLevel.Debug, fileSizeLimitBytes: 10000000); services.AddSingleton<IWalletManager, WalletManager>();
services.AddSingleton<WalletController>();
services.AddSingleton<ITracker, Tracker>(); });
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.AddSingleton<IWalletManager, WalletManager>();
services.AddSingleton<WalletController>();
});
}); });
return fullNodeBuilder; return fullNodeBuilder;
......
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Breeze.Wallet.Notifications;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NBitcoin; using NBitcoin;
using Stratis.Bitcoin; using Stratis.Bitcoin;
using Stratis.Bitcoin.Notifications; using Stratis.Bitcoin.Notifications;
using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Wallet;
namespace Breeze.Wallet namespace Breeze.Wallet
{ {
public class Tracker : ITracker public class LightWalletSyncManager : WalletSyncManager
{ {
private readonly WalletManager walletManager; private readonly WalletManager walletManager;
private readonly ConcurrentChain chain; private readonly ConcurrentChain chain;
private readonly Signals signals;
private readonly BlockNotification blockNotification; private readonly BlockNotification blockNotification;
private readonly CoinType coinType; private readonly CoinType coinType;
private readonly ILogger logger; private readonly ILogger logger;
public Tracker(ILoggerFactory loggerFactory, IWalletManager walletManager, ConcurrentChain chain, Signals signals, BlockNotification blockNotification, Network network) public LightWalletSyncManager(ILoggerFactory loggerFactory, IWalletManager walletManager, ConcurrentChain chain, Network network,
BlockNotification blockNotification):base(loggerFactory, walletManager, chain, network)
{ {
this.walletManager = walletManager as WalletManager;
this.chain = chain;
this.signals = signals;
this.blockNotification = blockNotification; this.blockNotification = blockNotification;
this.coinType = (CoinType)network.Consensus.CoinType;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task Initialize() public override void Initialize()
{ {
// get the chain headers. This needs to be up-to-date before we really do anything // get the chain headers. This needs to be up-to-date before we really do anything
await this.WaitForChainDownloadAsync(); this.WaitForChainDownloadAsync().GetAwaiter().GetResult();
// subscribe to receiving blocks and transactions
BlockSubscriber sub = new BlockSubscriber(this.signals.Blocks, new BlockObserver(this.chain, this.walletManager));
sub.Subscribe();
TransactionSubscriber txSub = new TransactionSubscriber(this.signals.Transactions, new TransactionObserver(this.walletManager));
txSub.Subscribe();
// start syncing blocks // start syncing blocks
var bestHeightForSyncing = this.FindBestHeightForSyncing(); var bestHeightForSyncing = this.FindBestHeightForSyncing();
this.SyncFrom(bestHeightForSyncing); this.SyncFrom(bestHeightForSyncing);
this.logger.LogInformation($"Tracker initialized. Syncing from {bestHeightForSyncing}.");
} }
private int FindBestHeightForSyncing() private int FindBestHeightForSyncing()
...@@ -91,7 +75,7 @@ namespace Breeze.Wallet ...@@ -91,7 +75,7 @@ namespace Breeze.Wallet
} }
/// <inheritdoc /> /// <inheritdoc />
public void SyncFrom(DateTime date) public override void SyncFrom(DateTime date)
{ {
int blockSyncStart = this.chain.GetHeightAtTime(date); int blockSyncStart = this.chain.GetHeightAtTime(date);
...@@ -100,7 +84,7 @@ namespace Breeze.Wallet ...@@ -100,7 +84,7 @@ namespace Breeze.Wallet
} }
/// <inheritdoc /> /// <inheritdoc />
public void SyncFrom(int height) public override void SyncFrom(int height)
{ {
this.blockNotification.SyncFrom(this.chain.GetBlock(height).HashBlock); this.blockNotification.SyncFrom(this.chain.GetBlock(height).HashBlock);
} }
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Breeze.Wallet.Models
{
public class RequestModel
{
public override string ToString()
{
return JsonConvert.SerializeObject(this, Formatting.Indented);
}
}
/// <summary>
/// Object used to create a new wallet
/// </summary>
public class WalletCreationRequest : RequestModel
{
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
public string Network { get; set; }
public string FolderPath { get; set; }
[Required(ErrorMessage = "The name of the wallet to create is missing.")]
public string Name { get; set; }
}
public class WalletLoadRequest : RequestModel
{
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
public string FolderPath { get; set; }
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string Name { get; set; }
}
public class WalletRecoveryRequest : RequestModel
{
[Required(ErrorMessage = "A mnemonic is required.")]
public string Mnemonic { get; set; }
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
public string FolderPath { get; set; }
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string Name { get; set; }
public string Network { get; set; }
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime CreationDate { get; set; }
}
public class WalletHistoryRequest : RequestModel
{
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string WalletName { get; set; }
[Required(ErrorMessage = "The type of coin for which history is requested is missing.")]
public CoinType CoinType { get; set; }
}
public class WalletBalanceRequest : RequestModel
{
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string WalletName { get; set; }
[Required(ErrorMessage = "The type of coin for which history is requested is missing.")]
public CoinType CoinType { get; set; }
}
public class WalletName : RequestModel
{
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string Name { get; set; }
}
public class BuildTransactionRequest : RequestModel
{
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string WalletName { get; set; }
[Required]
public CoinType CoinType { get; set; }
[Required(ErrorMessage = "The name of the account is missing.")]
public string AccountName { get; set; }
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
[Required(ErrorMessage = "A destination address is required.")]
public string DestinationAddress { get; set; }
[Required(ErrorMessage = "An amount is required.")]
public string Amount { get; set; }
[Required(ErrorMessage = "A fee type is required. It can be 'low', 'medium' or 'high'.")]
public string FeeType { get; set; }
public bool AllowUnconfirmed { get; set; }
}
public class SendTransactionRequest : RequestModel
{
[Required(ErrorMessage = "A transaction in hexadecimal format is required.")]
public string Hex { get; set; }
}
public class GetUnusedAddressModel : RequestModel
{
/// <summary>
/// The name of the wallet from which to get the address.
/// </summary>
[Required]
public string WalletName { get; set; }
/// <summary>
/// The type of coin this address is for.
/// </summary>
[Required]
public CoinType CoinType { get; set; }
/// <summary>
/// The name of the account for which to get the address.
/// </summary>
[Required]
public string AccountName { get; set; }
}
public class GetUnusedAccountModel : RequestModel
{
/// <summary>
/// The name of the wallet in which to create the account.
/// </summary>
[Required]
public string WalletName { get; set; }
/// <summary>
/// The type of coin this account contains.
/// </summary>
[Required]
public CoinType CoinType { get; set; }
/// <summary>
/// The password for this wallet.
/// </summary>
[Required]
public string Password { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletBalanceModel
{
[JsonProperty(PropertyName = "balances")]
public List<AccountBalance> AccountsBalances { get; set; }
}
public class AccountBalance
{
[JsonProperty(PropertyName = "accountName")]
public string Name { get; set; }
[JsonProperty(PropertyName = "accountHdPath")]
public string HdPath { get; set; }
[JsonProperty(PropertyName = "coinType")]
public CoinType CoinType { get; set; }
[JsonProperty(PropertyName = "amountConfirmed")]
public Money AmountConfirmed { get; set; }
[JsonProperty(PropertyName = "amountUnconfirmed")]
public Money AmountUnconfirmed { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletBuildTransactionModel
{
[JsonProperty(PropertyName = "fee")]
public Money Fee { get; set; }
[JsonProperty(PropertyName = "hex")]
public string Hex { get; set; }
[JsonProperty(PropertyName = "transactionId")]
public uint256 TransactionId { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletFileModel
{
[JsonProperty(PropertyName = "walletsPath")]
public string WalletsPath { get; set; }
[JsonProperty(PropertyName = "walletsFiles")]
public IEnumerable<string> WalletsFiles { get; set; }
}
}
using System;
using Breeze.Wallet.JsonConverters;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletGeneralInfoModel
{
[JsonProperty(PropertyName = "walletFilePath")]
public string WalletFilePath { get; set; }
[JsonProperty(PropertyName = "network")]
[JsonConverter(typeof(NetworkConverter))]
public Network Network { get; set; }
/// <summary>
/// The time this wallet was created.
/// </summary>
[JsonProperty(PropertyName = "creationTime")]
[JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset CreationTime { get; set; }
[JsonProperty(PropertyName = "isDecrypted")]
public bool IsDecrypted { get; set; }
/// <summary>
/// The height of the last block that was synced.
/// </summary>
[JsonProperty(PropertyName = "lastBlockSyncedHeight")]
public int? LastBlockSyncedHeight { get; set; }
/// <summary>
/// The total number of blocks.
/// </summary>
[JsonProperty(PropertyName = "chainTip")]
public int? ChainTip { get; set; }
/// <summary>
/// The total number of nodes that we're connected to.
/// </summary>
[JsonProperty(PropertyName = "connectedNodes")]
public int ConnectedNodes { get; set; }
}
}
using System;
using System.Collections.Generic;
using Breeze.Wallet.JsonConverters;
using NBitcoin;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace Breeze.Wallet.Models
{
public class WalletHistoryModel
{
[JsonProperty(PropertyName = "transactionsHistory")]
public List<TransactionItemModel> TransactionsHistory { get; set; }
}
public class TransactionItemModel
{
[JsonProperty(PropertyName = "type")]
[JsonConverter(typeof(StringEnumConverter), true)]
public TransactionItemType Type { get; set; }
/// <summary>
/// The Base58 representation of this address.
/// </summary>
[JsonProperty(PropertyName = "toAddress", NullValueHandling = NullValueHandling.Ignore)]
public string ToAddress { get; set; }
[JsonProperty(PropertyName = "id")]
[JsonConverter(typeof(UInt256JsonConverter))]
public uint256 Id { get; set; }
[JsonProperty(PropertyName = "amount")]
public Money Amount { get; set; }
/// <summary>
/// A list of payments made out in this transaction.
/// </summary>
[JsonProperty(PropertyName = "payments", NullValueHandling = NullValueHandling.Ignore)]
public ICollection<PaymentDetailModel> Payments { get; set; }
[JsonProperty(PropertyName = "fee", NullValueHandling = NullValueHandling.Ignore)]
public Money Fee { get; set; }
/// <summary>
/// The height of the block in which this transaction was confirmed.
/// </summary>
[JsonProperty(PropertyName = "confirmedInBlock", NullValueHandling = NullValueHandling.Ignore)]
public int? ConfirmedInBlock { get; set; }
[JsonProperty(PropertyName = "timestamp")]
[JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset Timestamp { get; set; }
}
public class PaymentDetailModel
{
/// <summary>
/// The Base58 representation of the destination address.
/// </summary>
[JsonProperty(PropertyName = "destinationAddress")]
public string DestinationAddress { get; set; }
/// <summary>
/// The transaction amount.
/// </summary>
[JsonProperty(PropertyName = "amount")]
[JsonConverter(typeof(MoneyJsonConverter))]
public Money Amount { get; set; }
}
public enum TransactionItemType
{
Received,
Send
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletModel
{
[JsonProperty(PropertyName = "network")]
public string Network { get; set; }
[JsonProperty(PropertyName = "fileName")]
public string FileName { get; set; }
[JsonProperty(PropertyName = "addresses")]
public IEnumerable<string> Addresses { get; set; }
}
}
using NBitcoin;
using Stratis.Bitcoin;
namespace Breeze.Wallet.Notifications
{
/// <summary>
/// Observer that receives notifications about the arrival of new <see cref="Block"/>s.
/// </summary>
public class BlockObserver : SignalObserver<Block>
{
private readonly ConcurrentChain chain;
private readonly IWalletManager walletManager;
public BlockObserver(ConcurrentChain chain, IWalletManager walletManager)
{
this.chain = chain;
this.walletManager = walletManager;
}
/// <summary>
/// Manages what happens when a new block is received.
/// </summary>
/// <param name="block">The new block</param>
protected override void OnNextCore(Block block)
{
var hash = block.Header.GetHash();
var height = this.chain.GetBlock(hash).Height;
this.walletManager.ProcessBlock(height, block);
}
}
}
using Stratis.Bitcoin;
using System;
using NBitcoin;
namespace Breeze.Wallet.Notifications
{
/// <summary>
/// Manages the subscription of the block observer to the block signaler.
/// </summary>
public class BlockSubscriber
{
private readonly ISignaler<Block> signaler;
private readonly BlockObserver observer;
public BlockSubscriber(ISignaler<Block> signaler, BlockObserver observer)
{
this.signaler = signaler;
this.observer = observer;
}
/// <summary>
/// Subscribes the block observer to the block signaler.
/// </summary>
/// <returns>An <see cref="IDisposable"/></returns>
public IDisposable Subscribe()
{
return this.signaler.Subscribe(this.observer);
}
}
}
using NBitcoin;
using Stratis.Bitcoin;
namespace Breeze.Wallet.Notifications
{
/// <summary>
/// Observer that receives notifications about the arrival of new <see cref="Transaction"/>s.
/// </summary>
public class TransactionObserver : SignalObserver<Transaction>
{
private readonly IWalletManager walletManager;
public TransactionObserver(IWalletManager walletManager)
{
this.walletManager = walletManager;
}
/// <summary>
/// Manages what happens when a new transaction is received.
/// </summary>
/// <param name="transaction">The new transaction</param>
protected override void OnNextCore(Transaction transaction)
{
this.walletManager.ProcessTransaction(transaction);
}
}
}
using Stratis.Bitcoin;
using System;
using NBitcoin;
namespace Breeze.Wallet.Notifications
{
/// <summary>
/// Manages the subscription of the transaction observer to the transaction signaler.
/// </summary>
public class TransactionSubscriber
{
private readonly ISignaler<Transaction> signaler;
private readonly TransactionObserver observer;
public TransactionSubscriber(ISignaler<Transaction> signaler, TransactionObserver observer)
{
this.signaler = signaler;
this.observer = observer;
}
/// <summary>
/// Subscribes the transaction observer to the transaction signaler.
/// </summary>
/// <returns>An <see cref="IDisposable"/></returns>
public IDisposable Subscribe()
{
return this.signaler.Subscribe(this.observer);
}
}
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment