Commit f7dcb1cb authored by Jeremy Bokobza's avatar Jeremy Bokobza

Replaced the Breeze.Api, Breeze.Api.Tests and the Breeze.Wallet projects to...

Replaced the Breeze.Api, Breeze.Api.Tests and the Breeze.Wallet projects to Stratis.Bitcoin.Api, Stratis.Bitcoin.Api.Tests and Stratis.bitcoin.Features.LightWallet projects.
Also moved all the submodule projects to an External folder
parent 8d9a4546
......@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807563C4-7259-434D-B604-A14C3DCF8E30}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{807563C4-7259-434D-B604-A14C3DCF8E30}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{2FF3130A-E8F3-4E0B-8733-B34C4A19910C}"
ProjectSection(SolutionItems) = preProject
......@@ -14,12 +14,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{2FF313
global.json = global.json
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Api", "src\Breeze.Api\Breeze.Api.csproj", "{E7B3E9EB-34E8-4B10-B296-4D5270E314A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Api.Tests", "src\Breeze.Api.Tests\Breeze.Api.Tests.csproj", "{BD5174B4-DCE8-4594-9A16-B83E56767770}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Wallet", "src\Breeze.Wallet\Breeze.Wallet.csproj", "{D16CD478-9D1E-4C69-91AD-43539E94A215}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Breeze.Daemon", "src\Breeze.Daemon\Breeze.Daemon.csproj", "{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin", "..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj", "{C93CB29F-C0A1-40D6-A4CC-B93C75423ED5}"
......@@ -30,24 +24,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NBitcoin", "..\StratisBitco
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HashLib", "..\StratisBitcoinFullNode\NStratis\Hashing\HashLib\HashLib.csproj", "{25A26C47-210F-4B7F-A48C-71760D0C9475}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin.Api", "..\StratisBitcoinFullNode\Stratis.Bitcoin.Api\Stratis.Bitcoin.Api.csproj", "{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin.Features.LightWallet", "..\StratisBitcoinFullNode\Stratis.Bitcoin.Features.LightWallet\Stratis.Bitcoin.Features.LightWallet.csproj", "{64EF5766-40C7-46D4-87AA-ED16D0F867B1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4}.Release|Any CPU.Build.0 = Release|Any CPU
{BD5174B4-DCE8-4594-9A16-B83E56767770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD5174B4-DCE8-4594-9A16-B83E56767770}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD5174B4-DCE8-4594-9A16-B83E56767770}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD5174B4-DCE8-4594-9A16-B83E56767770}.Release|Any CPU.Build.0 = Release|Any CPU
{D16CD478-9D1E-4C69-91AD-43539E94A215}.Debug|Any CPU.ActiveCfg = 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.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
......@@ -68,14 +54,24 @@ Global
{25A26C47-210F-4B7F-A48C-71760D0C9475}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25A26C47-210F-4B7F-A48C-71760D0C9475}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25A26C47-210F-4B7F-A48C-71760D0C9475}.Release|Any CPU.Build.0 = Release|Any CPU
{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB}.Release|Any CPU.Build.0 = Release|Any CPU
{64EF5766-40C7-46D4-87AA-ED16D0F867B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64EF5766-40C7-46D4-87AA-ED16D0F867B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64EF5766-40C7-46D4-87AA-ED16D0F867B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64EF5766-40C7-46D4-87AA-ED16D0F867B1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E7B3E9EB-34E8-4B10-B296-4D5270E314A4} = {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}
{AAF6163B-1BE2-48CE-9F9F-577C6D7AAB8D} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{C93CB29F-C0A1-40D6-A4CC-B93C75423ED5} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{330D169F-1C9A-41B4-834B-AE363FF85811} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{05787CEF-6A8F-4FF5-A57C-C8A5D4F6C2DB} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{25A26C47-210F-4B7F-A48C-71760D0C9475} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{D541577E-2AB1-4379-BA6D-2F23F0B1DFBB} = {807563C4-7259-434D-B604-A14C3DCF8E30}
{64EF5766-40C7-46D4-87AA-ED16D0F867B1} = {807563C4-7259-434D-B604-A14C3DCF8E30}
EndGlobalSection
EndGlobal
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>Breeze.Api.Tests</AssemblyName>
<PackageId>Breeze.Api.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<PackageTargetFallback>$(PackageTargetFallback);netcore50</PackageTargetFallback>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" />
<ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" />
<PackageReference Include="Moq" Version="4.7.10" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
{
"variables": [],
"info": {
"name": "Node",
"_postman_id": "28053655-d992-60bb-a476-0c83186f5674",
"description": "",
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
},
"item": [
{
"name": "Connect full node",
"request": {
"url": "http://localhost:5000/api/node/connect",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{ \n\t\"password\": \"123456\",\n\t\"network\": \"Main\",\n\t\"folderPath\": \"Wallets\",\n\t\"name\": \"myFirstWallet\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "Start syncing from hash",
"request": {
"url": "http://localhost:5000/api/node/sync",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{ \n\t\"hash\": \"00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886\"\n}"
},
"description": "{ \n\t\"hash\": \"00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886\"\n}"
},
"response": []
}
]
}
\ No newline at end of file
{
"variables": [],
"info": {
"name": "Wallet",
"_postman_id": "b5720ab4-24a5-6957-0ea6-766a9cbaf488",
"description": "Requests relating to operations on the wallet",
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
},
"item": [
{
"name": "Create wallet - success",
"request": {
"url": "http://localhost:5000/api/wallet/create",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{ \n\t\"password\": \"123456\",\n\t\"network\": \"testnet\",\n\t\"name\": \"testwallet\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "Create wallet - validation errors",
"request": {
"url": "http://localhost:5000/api/wallet/create",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "\n{\n\t\"network\": \"Main\",\n\t\"localFilePath\": \"Wallets/TestWallet2.json\"\n}"
},
"description": "with errors"
},
"response": []
},
{
"name": "Load wallet",
"request": {
"url": "http://localhost:5000/api/wallet/load",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{ \n\t\"password\": \"123456\",\n\t\"folderPath\": \"Wallets\",\n\t\"name\": \"myFirstWadllet\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "Recover wallet",
"request": {
"url": "http://localhost:5000/api/wallet/recover",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{ \n\t\"password\": \"123456\",\n\t\"network\": \"Main\",\n\t\"folderPath\": \"Wallets\",\n\t\"name\": \"myRecoveredWallet\",\n\t\"mnemonic\": \"elbow scale error joke labor page beyond curve indicate exit brass laundry\",\n\t\"creationDate\": \"2016-02-25 16:20:33\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "Get wallet Info",
"request": {
"url": "http://localhost:5000/api/wallet/general-info?name=mywallet",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": ""
},
"description": ""
},
"response": []
},
{
"name": "Get Wallet History",
"request": {
"url": "http://localhost:5000/api/wallet/history?walletname=wallet1&cointype=0",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": ""
},
"description": ""
},
"response": []
},
{
"name": "Get wallet balance",
"request": {
"url": "http://localhost:5000/api/wallet/balance?walletname=wallet1&cointype=0",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": ""
},
"description": ""
},
"response": []
},
{
"name": "Build transaction",
"request": {
"url": "http://localhost:5000/api/wallet/build-transaction",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{\r\n\t\"walletName\": \"testwallet\",\r\n\t\"accountName\": \"account 0\",\r\n\t\"coinType\": 1,\r\n \"password\": \"password\",\r\n \"destinationAddress\": \"1FYp9uguYCz7DgSF9jTWDeZF8kdRKQTXPg\",\r\n \"amount\": \"0.12\",\r\n \"feeType\": \"low\",\r\n \"allowUnconfirmed\": \"true\"\r\n}"
},
"description": ""
},
"response": []
},
{
"name": "Send transaction",
"request": {
"url": "http://localhost:5000/api/wallet/send-transaction",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"hex\": \"01000000061b1ca819e76f9131b23335ec905ffc5fc27e36a7843a5b7c6d1b455b904359f7000000006b483045022100c11f78ce7f02b2312b6675d3ad99cec6ede879d446c2b14628ef4f8ce9b3fdc5022073649a14971568a1cd2aa84b5dd404645f29e49882f60a9642850539443872fe012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffff1d3c389af5fdd047e307e5d5f87656bb2ef0c40b6ee879d342a59192090d3fbc000000006b483045022100dc7e0445fe98f3e76d68906c640ecca597598a03b48e6b85d72918347b9da7330220340ce9e9533ea84375a1f2122b7868b8ab556da53f1e1af14d1a71b0b123aade012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffff1ed624bad3df9d3a7be56dcd5d97c996fccc78164f16f59658b33f8da8859deb000000006b483045022100ba2a55f55a37b6712dd25dbef411aed869190ef60a208b39d4bd8e0ce8635b4d02201976d63489e23205aab651a9def43d6b3a740ba06de2ecabc43504241a71f229012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffff24e5cb4893beb0bb60193dbe11a9778d07e127c1cbc939c0a0388b1013ef75d9000000006a47304402203c27eea34db0ba070bee38d625d2cfcec1a0f5d8a9124023c84e9963d37f6145022015f7657cc57be515e6aa43c93c73457f5583b7c90c0af4e8a2f913257df27b0b012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffff997e6738c45eaf7af8bed7dc09e258139bffb0d2be8b4167473b6943adc0b28b000000006a47304402202d4c6df39b725d571d67bef14f0c6baa0cf4b93aa54aac2d2a15d3d940510d0602203643162545d5b63c007986627a317ed962f4d5023e4c15e9636a4eede86930c7012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffffcff2021b6b0bcd2a8b38583539dc140b98da4f41ae1e4adb089dc2cf3b66d6c6000000006a473044022019d5264c99145c7203e690fb2f57b0e218af2761e024f9ec1b774c703939b96e02204c2430fc4ae0fa43afb19a722f7b5d706bf5f2d5ee85229cbdc7a7b26433f5fd012102a41e4348bb233e40cf3a3402e2dc92a31b69ef56090fff242aa7e4bff828929fffffffff018f73e606000000001976a914ec093b0943ec524769553e1b7261b67ecab47e8688ac00000000\"\n}"
},
"description": ""
},
"response": []
},
{
"name": "Get wallets files",
"request": {
"url": "http://localhost:5000/api/wallet/files",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": ""
},
"description": "Gets all the wallets files stored in the default folder"
},
"response": []
},
{
"name": "Get unused account in wallet",
"request": {
"url": "http://localhost:5000/api/wallet/account",
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"walletName\": \"myFirstWallet\",\n\t\"password\": \"123456\",\n\t\"coinType\": 0\n}"
},
"description": ""
},
"response": []
},
{
"name": "Get unused address in wallet",
"request": {
"url": "http://localhost:5000/api/wallet/address?walletName=wallet1&accountName=account 0&coinType=0",
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"description": ""
}
],
"body": {
"mode": "raw",
"raw": ""
},
"description": ""
},
"response": []
}
]
}
\ No newline at end of file
using System;
using System.Threading.Tasks;
using Breeze.Api.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Builder.Feature;
using Stratis.Bitcoin.Utilities;
namespace Breeze.Api
{
/// <summary>
/// Provides an Api to the full node
/// </summary>
public class ApiFeature : FullNodeFeature
{
private readonly IFullNodeBuilder fullNodeBuilder;
private readonly FullNode fullNode;
private readonly ApiFeatureOptions apiFeatureOptions;
private readonly IAsyncLoopFactory asyncLoopFactory;
private readonly ILogger logger;
public ApiFeature(
IFullNodeBuilder fullNodeBuilder,
FullNode fullNode,
ApiFeatureOptions apiFeatureOptions,
IAsyncLoopFactory asyncLoopFactory,
ILoggerFactory loggerFactory)
{
this.fullNodeBuilder = fullNodeBuilder;
this.fullNode = fullNode;
this.apiFeatureOptions = apiFeatureOptions;
this.asyncLoopFactory = asyncLoopFactory;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
}
public override void Start()
{
this.logger.LogInformation($"Api starting on url {this.fullNode.Settings.ApiUri}");
Program.Initialize(this.fullNodeBuilder.Services, this.fullNode);
this.TryStartKeepaliveMonitor();
}
/// <summary>
/// A KeepaliveMonitor when enabled will shutdown the
/// node if no one is calling the keepalive endpoint
/// during a certain trashold window
/// </summary>
public void TryStartKeepaliveMonitor()
{
if (this.apiFeatureOptions.KeepaliveMonitor?.KeepaliveInterval.TotalSeconds > 0)
{
this.asyncLoopFactory.Run("ApiFeature.KeepaliveMonitor", token =>
{
// shortened for redability
var monitor = this.apiFeatureOptions.KeepaliveMonitor;
// check the trashold to trigger a shutdown
if (monitor.LastBeat.Add(monitor.KeepaliveInterval) < DateTime.UtcNow)
this.fullNode.Stop();
return Task.CompletedTask;
},
this.fullNode.NodeLifetime.ApplicationStopping,
repeatEvery: this.apiFeatureOptions.KeepaliveMonitor?.KeepaliveInterval,
startAfter: TimeSpans.Minute);
}
}
}
public class ApiFeatureOptions
{
public KeepaliveMonitor KeepaliveMonitor { get; set; }
public void Keepalive(TimeSpan timeSpan)
{
this.KeepaliveMonitor = new KeepaliveMonitor {KeepaliveInterval = timeSpan};
}
}
public static class ApiFeatureExtension
{
public static IFullNodeBuilder UseApi(this IFullNodeBuilder fullNodeBuilder, Action<ApiFeatureOptions> optionsAction = null)
{
// TODO: move the options in to the feature builder
var options = new ApiFeatureOptions();
optionsAction?.Invoke(options);
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<ApiFeature>()
.FeatureServices(services =>
{
services.AddSingleton(fullNodeBuilder);
services.AddSingleton(options);
});
});
return fullNodeBuilder;
}
}
}
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Breeze.Api</AssemblyName>
<OutputType>Library</OutputType>
<PackageId>Breeze.Api</PackageId>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
<ApplicationIcon />
<OutputTypeEx>library</OutputTypeEx>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>..\\Breeze.Daemon\\bin\\Debug\\netcoreapp1.0\\Breeze.Api.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>..\\Breeze.Daemon\\bin\\Release\\netcoreapp1.0\\Breeze.Api.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" 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.Options.ConfigurationExtensions" Version="1.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
</Project>
using System;
using Microsoft.AspNetCore.Mvc;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Builder;
namespace Breeze.Api.Controllers
{
[Route("api/[controller]")]
public class NodeController : Controller
{
private readonly IFullNode fullNode;
private readonly ApiFeatureOptions apiFeatureOptions;
public NodeController(IFullNode fullNode, ApiFeatureOptions apiFeatureOptions)
{
this.fullNode = fullNode;
this.apiFeatureOptions = apiFeatureOptions;
}
/// <summary>
/// Returns some general information about the status of the underlying node.
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("status")]
public IActionResult Status()
{
return this.NotFound();
}
/// <summary>
/// Trigger a shoutdown of the current running node.
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("shutdown")]
public IActionResult Shutdown()
{
// start the node shutdown process
this.fullNode.Stop();
return this.Ok();
}
/// <summary>
/// Set the keepalive flag.
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("keepalive")]
public IActionResult Keepalive()
{
if (this.apiFeatureOptions.KeepaliveMonitor == null)
return new ObjectResult("Keepalive Disabled") {StatusCode = 405}; // (405) Method Not Allowed
this.apiFeatureOptions.KeepaliveMonitor.LastBeat = DateTime.UtcNow;
return this.Ok();
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
namespace Breeze.Api
{
/// <summary>
/// An asynchronous action filter whose role is to log details from the Http requests to the API.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter" />
public class LoggingActionFilter : IAsyncActionFilter
{
private readonly ILogger logger;
public LoggingActionFilter(ILoggerFactory loggerFactory)
{
this.logger = loggerFactory.CreateLogger("api.request.logger");
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
HttpRequest request = context.HttpContext.Request;
// get the body
var body = string.Empty;
var arguments = context.ActionArguments;
if (request.ContentLength != null && arguments != null && arguments.Any())
{
body = string.Join(Environment.NewLine, arguments.Values);
}
this.logger.LogDebug($"Received {request.Method} {request.GetDisplayUrl()}. Body: '{body}'");
await next();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Breeze.Api.Models
{
public class KeepaliveMonitor
{
public DateTime LastBeat { get; set; }
public TimeSpan KeepaliveInterval { get; set; }
}
}
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace Breeze.Api
{
public static class MvcBuilderExtensions
{
/// <summary>
/// Finds all the types that are <see cref="Controller"/> and add them to the Api as services.
/// </summary>
/// <param name="builder">The builder</param>
/// <param name="services">The services to look into</param>
/// <returns>The Mvc builder</returns>
public static IMvcBuilder AddControllers(this IMvcBuilder builder, IServiceCollection services)
{
var controllerTypes = services.Where(s => s.ServiceType.GetTypeInfo().BaseType == typeof(Controller));
foreach (var controllerType in controllerTypes)
{
builder.AddApplicationPart(controllerType.ServiceType.GetTypeInfo().Assembly);
}
builder.AddControllersAsServices();
return builder;
}
}
}
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin;
namespace Breeze.Api
{
public class Program
{
public static void Main(string[] args)
{
}
public static void Initialize(IEnumerable<ServiceDescriptor> services, FullNode fullNode)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(fullNode.Settings.ApiUri.ToString())
.ConfigureServices(collection =>
{
if (services == null || fullNode == null)
{
return;
}
// copies all the services defined for the full node to the Api.
// also copies over singleton instances already defined
foreach (var service in services)
{
var obj = fullNode.Services.ServiceProvider.GetService(service.ServiceType);
if (obj != null && service.Lifetime == ServiceLifetime.Singleton && service.ImplementationInstance == null)
{
collection.AddSingleton(service.ServiceType, obj);
}
else
{
collection.Add(service);
}
}
})
.UseStartup<Startup>()
.Build();
host.Start();
}
}
}
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:43190/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Breeze.Api": {
"commandName": "Project",
"launchUrl": "http://localhost:5000/api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
\ No newline at end of file
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Swashbuckle.AspNetCore.Swagger;
namespace Breeze.Api
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add service and create Policy to allow Cross-Origin Requests
services.AddCors
(
options =>
{
options.AddPolicy
(
"CorsPolicy",
builder =>
{
var allowedDomains = new[] { "http://localhost", "http://localhost:4200" };
builder
.WithOrigins(allowedDomains)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}
);
});
// Add framework services.
services.AddMvc(options => options.Filters.Add(typeof(LoggingActionFilter)))
// add serializers for NBitcoin objects
.AddJsonOptions(options => NBitcoin.JsonConverters.Serializer.RegisterFrontConverters(options.SerializerSettings))
.AddControllers(services);
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(setup =>
{
setup.SwaggerDoc("v1", new Info { Title = "Breeze.Api", Version = "v1" });
//Set the comments path for the swagger json and ui.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var apiXmlPath = Path.Combine(basePath, "Breeze.Api.xml");
var walletXmlPath = Path.Combine(basePath, "Breeze.Wallet.xml");
setup.IncludeXmlComments(apiXmlPath);
setup.IncludeXmlComments(walletXmlPath);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("CorsPolicy");
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Breeze.Api V1");
});
}
}
}
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>
......@@ -13,9 +13,9 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin.Api\Stratis.Bitcoin.Api.csproj" />
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin.Features.LightWallet\Stratis.Bitcoin.Features.LightWallet.csproj" />
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\Breeze.Api\Breeze.Api.csproj" />
<ProjectReference Include="..\Breeze.Wallet\Breeze.Wallet.csproj" />
</ItemGroup>
<ItemGroup>
......
using System;
using System.Linq;
using Breeze.Api;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration;
using Breeze.Wallet;
using NBitcoin;
using NBitcoin.Protocol;
using Stratis.Bitcoin.Api;
using Stratis.Bitcoin.Features.BlockStore;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.LightWallet;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.Miner;
using Stratis.Bitcoin.Features.Notifications;
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<AssemblyName>Breeze.Wallet</AssemblyName>
<PackageId>Breeze.Wallet</PackageId>
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>..\\Breeze.Daemon\\bin\\Debug\\netcoreapp1.0\\Breeze.Wallet.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>..\\Breeze.Daemon\\bin\\Release\\netcoreapp1.0\\Breeze.Wallet.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="1.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
</Project>
using System.Threading;
using System.Threading.Tasks;
using Stratis.Bitcoin.Builder.Feature;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using NBitcoin.Protocol;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Connection;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Common.Hosting;
using Stratis.Bitcoin.Features.Consensus.Deployments;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.Wallet.Controllers;
namespace Breeze.Wallet
{
public class LightWalletFeature : FullNodeFeature
{
private readonly IWalletSyncManager walletSyncManager;
private readonly IWalletManager walletManager;
private readonly IConnectionManager connectionManager;
private readonly ConcurrentChain chain;
private readonly NodeDeployments nodeDeployments;
private readonly IAsyncLoopFactory asyncLoopFactory;
private readonly INodeLifetime nodeLifetime;
public LightWalletFeature(IWalletSyncManager walletSyncManager, IWalletManager walletManager, IConnectionManager connectionManager,
ConcurrentChain chain, NodeDeployments nodeDeployments, IAsyncLoopFactory asyncLoopFactory, INodeLifetime nodeLifetime)
{
this.walletSyncManager = walletSyncManager;
this.walletManager = walletManager;
this.connectionManager = connectionManager;
this.chain = chain;
this.nodeDeployments = nodeDeployments;
this.asyncLoopFactory = asyncLoopFactory;
this.nodeLifetime = nodeLifetime;
}
public override void Start()
{
this.connectionManager.Parameters.TemplateBehaviors.Add(new DropNodesBehaviour(this.chain, this.connectionManager));
this.walletManager.Initialize();
this.walletSyncManager.Initialize();
this.StartDeploymentsChecksLoop();
}
public void StartDeploymentsChecksLoop()
{
var loopToken = CancellationTokenSource.CreateLinkedTokenSource(this.nodeLifetime.ApplicationStopping);
this.asyncLoopFactory.Run("LightWalletFeature.CheckDeployments", token =>
{
if(!this.chain.IsDownloaded())
return Task.CompletedTask;
// check segwit activation on the chain of headers
// if segwit is active signal to only connect to
// nodes that also signal they are segwit nodes
var flags = this.nodeDeployments.GetFlags(this.walletSyncManager.WalletTip);
if (flags.ScriptFlags.HasFlag(ScriptVerify.Witness))
this.connectionManager.AddDiscoveredNodesRequirement(NodeServices.NODE_WITNESS);
// done checking
loopToken.Cancel();
return Task.CompletedTask;
},
loopToken.Token,
repeatEvery: TimeSpans.TenSeconds,
startAfter: TimeSpans.TenSeconds);
}
public override void Stop()
{
base.Stop();
}
}
public static class LightWalletFeatureExtension
{
public static IFullNodeBuilder UseLightWallet(this IFullNodeBuilder fullNodeBuilder)
{
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<LightWalletFeature>()
.FeatureServices(services =>
{
services.AddSingleton<IWalletSyncManager, LightWalletSyncManager>();
services.AddSingleton<IWalletManager, WalletManager>();
services.AddSingleton<IWalletFeePolicy, LightWalletFeePolicy>();
services.AddSingleton<WalletController>();
});
});
return fullNodeBuilder;
}
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Common.Hosting;
using Stratis.Bitcoin.Features.Notifications;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.Wallet.Notifications;
using Stratis.Bitcoin.Signals;
namespace Breeze.Wallet
{
public class LightWalletSyncManager : IWalletSyncManager
{
private readonly WalletManager walletManager;
private readonly ConcurrentChain chain;
private readonly BlockNotification blockNotification;
private readonly CoinType coinType;
private readonly ILogger logger;
private readonly Signals signals;
private ChainedBlock walletTip;
private readonly INodeLifetime nodeLifetime;
private readonly IAsyncLoopFactory asyncLoopFactory;
public ChainedBlock WalletTip => this.walletTip;
public LightWalletSyncManager(
ILoggerFactory loggerFactory,
IWalletManager walletManager,
ConcurrentChain chain,
Network network,
BlockNotification blockNotification,
Signals signals,
INodeLifetime nodeLifetime,
IAsyncLoopFactory asyncLoopFactory)
{
this.walletManager = walletManager as WalletManager;
this.chain = chain;
this.signals = signals;
this.blockNotification = blockNotification;
this.coinType = (CoinType)network.Consensus.CoinType;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
this.nodeLifetime = nodeLifetime;
this.asyncLoopFactory = asyncLoopFactory;
}
/// <inheritdoc />
public Task Initialize()
{
// subscribe to receiving blocks and transactions
BlockSubscriber sub = new BlockSubscriber(this.signals.Blocks, new BlockObserver(this));
sub.Subscribe();
TransactionSubscriber txSub = new TransactionSubscriber(this.signals.Transactions, new TransactionObserver(this));
txSub.Subscribe();
// if there is no wallet created yet, the wallet tip is the chain tip.
if (!this.walletManager.Wallets.Any())
{
this.walletTip = this.chain.Tip;
}
else
{
this.walletTip = this.chain.GetBlock(this.walletManager.WalletTipHash);
if (this.walletTip == null && this.chain.Height > 0)
{
// the wallet tip was not found in the main chain.
// this can happen if the node crashes unexpectedly.
// to recover we need to find the first common fork
// with the best chain, as the wallet does not have a
// list of chain headers we use a BlockLocator and persist
// that in the wallet. the block locator will help finding
// a common fork and bringing the wallet back to a good
// state (behind the best chain)
var locators = this.walletManager.Wallets.First().BlockLocator;
BlockLocator blockLocator = new BlockLocator { Blocks = locators.ToList() };
var fork = this.chain.FindFork(blockLocator);
this.walletManager.RemoveBlocks(fork);
this.walletManager.WalletTipHash = fork.HashBlock;
this.walletTip = fork;
this.logger.LogWarning($"Wallet tip was out of sync, wallet tip reverted back to Height = {this.walletTip.Height} hash = {this.walletTip.HashBlock}.");
}
// we're looking from where to start syncing the wallets.
// we start by looking at the heights of the wallets and we start syncing from the oldest one (the smallest height).
// if for some reason we can't find a height, we look at the creation date of the wallets and we start syncing from the earliest date.
int? earliestWalletHeight = this.walletManager.Wallets.Min(w => w.AccountsRoot.Single(a => a.CoinType == this.coinType).LastBlockSyncedHeight);
if (earliestWalletHeight == null)
{
DateTimeOffset oldestWalletDate = this.walletManager.Wallets.Min(w => w.CreationTime);
this.SyncFrom(oldestWalletDate.LocalDateTime);
}
else
{
this.SyncFrom(earliestWalletHeight.Value);
}
}
return Task.CompletedTask;
}
/// <inheritdoc />
public void ProcessBlock(Block block)
{
// if the new block previous hash is the same as the
// wallet hash then just pass the block to the manager
if (block.Header.HashPrevBlock != this.walletTip.HashBlock)
{
// if previous block does not match there might have
// been a reorg, check if the wallet is still on the main chain
ChainedBlock inBestChain = this.chain.GetBlock(this.walletTip.HashBlock);
if (inBestChain == null)
{
// the current wallet hash was not found on the main chain
// a reorg happenend so bring the wallet back top the last known fork
var fork = this.walletTip;
// we walk back the chained block object to find the fork
while (this.chain.GetBlock(fork.HashBlock) == null)
fork = fork.Previous;
Guard.Assert(fork.HashBlock == block.Header.HashPrevBlock);
this.walletManager.RemoveBlocks(fork);
this.logger.LogWarning($"Reorg detected, wallet tip reverted back to Height = {fork.Height} hash = {fork.HashBlock}.");
}
else
{
ChainedBlock incomingBlock = this.chain.GetBlock(block.GetHash());
if (incomingBlock.Height > this.walletTip.Height)
{
// the wallet is falling behind we need to catch up
this.logger.LogWarning($"block received with height: {inBestChain.Height} and hash: {block.Header.GetHash()} is too far in advance. put the puller back.");
this.blockNotification.SyncFrom(this.walletTip.HashBlock);
return;
}
}
}
this.walletTip = this.chain.GetBlock(block.GetHash());
this.walletManager.ProcessBlock(block, this.walletTip);
}
/// <inheritdoc />
public void ProcessTransaction(Transaction transaction)
{
this.walletManager.ProcessTransaction(transaction);
}
/// <inheritdoc />
public void SyncFrom(DateTime date)
{
// before we start syncing we need to make sure that the chain is at a certain level.
// if the chain is behind the date from which we want to sync, we wait for it to catch up, and then we start syncing.
// if the chain is already past the date we want to sync from, we don't wait, even though the chain might not be fully downloaded.
if (this.chain.Tip.Header.BlockTime.LocalDateTime < date)
{
this.asyncLoopFactory.RunUntil("WalletFeature.DownloadChain", this.nodeLifetime.ApplicationStopping,
() => this.chain.Tip.Header.BlockTime.LocalDateTime >= date,
() => this.StartSync(this.chain.GetHeightAtTime(date)),
(ex) =>
{
// in case of an exception while waiting for the chain to be at a certain height, we just cut our losses and
// sync from the current height.
this.logger.LogError($"Exception occurred while waiting for chain to download: {ex.Message}");
this.StartSync(this.chain.Tip.Height);
},
TimeSpans.FiveSeconds);
}
else
{
this.StartSync(this.chain.GetHeightAtTime(date));
}
}
/// <inheritdoc />
public void SyncFrom(int height)
{
// before we start syncing we need to make sure that the chain is at a certain level.
// if the chain is behind the height from which we want to sync, we wait for it to catch up, and then we start syncing.
// if the chain is already past the height we want to sync from, we don't wait, even though the chain might not be fully downloaded.
if (this.chain.Tip.Height < height)
{
this.asyncLoopFactory.RunUntil("WalletFeature.DownloadChain", this.nodeLifetime.ApplicationStopping,
() => this.chain.Tip.Height >= height,
() => this.StartSync(height),
(ex) =>
{
// in case of an exception while waiting for the chain to be at a certain height, we just cut our losses and
// sync from the current height.
this.logger.LogError($"Exception occurred while waiting for chain to download: {ex.Message}");
this.StartSync(this.chain.Tip.Height);
},
TimeSpans.FiveSeconds);
}
else
{
this.StartSync(height);
}
}
private void StartSync(int height)
{
// TODO add support for the case where there is a reorg, like in the initialize method
var chainedBlock = this.chain.GetBlock(height);
if (chainedBlock == null)
throw new WalletException("Invalid block height");
this.walletTip = chainedBlock;
this.walletManager.WalletTipHash = chainedBlock.HashBlock;
this.blockNotification.SyncFrom(chainedBlock.HashBlock);
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Breeze.Wallet")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d16cd478-9d1e-4c69-91ad-43539e94a215")]
using System;
using NBitcoin;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.Wallet;
namespace Breeze.Wallet
{
public class LightWalletFeePolicy : IWalletFeePolicy
{
private readonly FeeRate minTxFee;
private readonly Money maxTxFee;
private readonly FeeRate highxFeePerKb;
private readonly FeeRate mediumTxFeePerKb;
private readonly FeeRate lowTxFeePerKb;
public LightWalletFeePolicy()
{
// when estimating the fee, the fee of each transactions needs to be knowen
// as this is an estimator on a light wallet we dont have the UTXO set
// that leaves with few options:
// - an external service,
// - hard code fee per kb
// - we may even be able to monitor the size of the mempool (mini mempool)
// (not the entire trx) and try to estimate based on pending count
// TODO: make fee values confugurable on startup
this.highxFeePerKb = new FeeRate(385939);
this.mediumTxFeePerKb = new FeeRate(245347);
this.lowTxFeePerKb = new FeeRate(171274);
this.maxTxFee = new Money(0.1M, MoneyUnit.BTC);
this.minTxFee = new FeeRate(1000);
}
public Money GetRequiredFee(int txBytes)
{
return Math.Max(this.minTxFee.GetFee(txBytes), MempoolValidator.MinRelayTxFee.GetFee(txBytes));
}
public Money GetMinimumFee(int txBytes, int confirmTarget)
{
// payTxFee is the user-set global for desired feerate
return this.GetMinimumFee(txBytes, confirmTarget, this.lowTxFeePerKb.GetFee(txBytes));
}
public Money GetMinimumFee(int txBytes, int confirmTarget, Money targetFee)
{
Money feeNeeded = targetFee;
feeNeeded = this.lowTxFeePerKb.GetFee(txBytes);
if (confirmTarget < 50) feeNeeded = this.mediumTxFeePerKb.GetFee(txBytes);
if (confirmTarget < 20) feeNeeded = this.highxFeePerKb.GetFee(txBytes);
// prevent user from paying a fee below minRelayTxFee or minTxFee
feeNeeded = Math.Max(feeNeeded, this.GetRequiredFee(txBytes));
// But always obey the maximum
if (feeNeeded > this.maxTxFee)
feeNeeded = this.maxTxFee;
return feeNeeded;
}
}
}
Subproject commit ca6dd380457e8ac43596630c02d5b37ce3e545a2
Subproject commit d6c79e304362fd9144a887f9c97a7dbb5d47a628
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