Commit d9300cfd authored by Dan Gershony's avatar Dan Gershony Committed by GitHub

Merge pull request #186 from bokobza/master

Part 2 of Moving Breeze projects to the full node
parents 2aa67b86 890ce56a
......@@ -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;
......
using Breeze.TumbleBit.Client;
using NBitcoin;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Signals;
namespace Breeze.TumbleBit
{
/// <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 ITumbleBitManager tumbleBitManager;
public BlockObserver(ConcurrentChain chain, ITumbleBitManager tumbleBitManager)
{
this.chain = chain;
this.tumbleBitManager = tumbleBitManager;
}
/// <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.tumbleBitManager.ProcessBlock(height, block);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<AssemblyName>Breeze.TumbleBit.Client</AssemblyName>
<PackageId>Breeze.TumbleBit.Client</PackageId>
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<PreBuildEvent></PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETSTANDARD1_6</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Flurl.Http" Version="1.1.2" />
<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" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin.Common\Stratis.Bitcoin.Common.csproj" />
<ProjectReference Include="..\..\..\StratisBitcoinFullNode\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
<ProjectReference Include="..\NTumbleBit\NTumbleBit.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Breeze.TumbleBit.Client;
using Breeze.TumbleBit.Models;
using Stratis.Bitcoin.Common.JsonErrors;
namespace Breeze.TumbleBit.Controllers
{
/// <summary>
/// Controller providing TumbleBit operations.
/// </summary>
[Route("api/[controller]")]
public class TumbleBitController : Controller
{
private readonly ITumbleBitManager tumbleBitManager;
public TumbleBitController(ITumbleBitManager tumbleBitManager)
{
this.tumbleBitManager = tumbleBitManager;
}
/// <summary>
/// Connect to a tumbler.
/// </summary>
[Route("connect")]
[HttpPost]
public async Task<IActionResult> ConnectAsync([FromBody] TumblerConnectionRequest request)
{
// checks the request is valid
if (!this.ModelState.IsValid)
{
var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors));
}
try
{
var tumblerParameters = await this.tumbleBitManager.ConnectToTumblerAsync(request.ServerAddress);
return this.Json(tumblerParameters);
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"An error occured connecting to the tumbler with uri {request.ServerAddress}.", e.ToString());
}
}
/// <summary>
/// Connect to a tumbler.
/// </summary>
[Route("tumble")]
[HttpPost]
public async Task<IActionResult> TumbleAsync([FromBody] TumbleRequest request)
{
// checks the request is valid
if (!this.ModelState.IsValid)
{
var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors));
}
try
{
await this.tumbleBitManager.TumbleAsync(request.OriginWalletName, request.DestinationWalletName);
return this.Ok();
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "An error occured starting tumbling session.", e.ToString());
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using NTumbleBit;
using Stratis.Bitcoin.Features.MemoryPool;
namespace Breeze.TumbleBit.Client
{
public class ExternalServices
{
public Transaction FundTransaction(TxOut txOut, FeeRate feeRate)
{
return null;
}
public bool Broadcast(Transaction tx)
{
return true;
}
public void TrustedBroadcast(int cycleStart, TransactionType transactionType, uint correlation, TrustedBroadcastRequest broadcast)
{
}
public TransactionInformation[] GetTransactions(Script scriptPubKey, bool withProof)
{
return null;
}
public FeeRate GetFeeRate()
{
decimal relayFee = MempoolValidator.MinRelayTxFee.FeePerK.ToUnit(MoneyUnit.BTC);
var minimumRate = new FeeRate(Money.Coins(relayFee * 2), 1000); //0.00002000 BTC/kB
var fallbackFeeRate = new FeeRate(Money.Satoshis(50), 1); //0.00050000 BTC/kB
// TODO add real fee estimation
//var rate = _RPCClient.TryEstimateFeeRate(1) ??
// _RPCClient.TryEstimateFeeRate(2) ??
// _RPCClient.TryEstimateFeeRate(3) ??
// FallBackFeeRate;
//if (rate < MinimumFeeRate)
// rate = MinimumFeeRa
return fallbackFeeRate;
}
}
}
namespace Breeze.TumbleBit.Client
{
public interface IStateMachine
{
/// <summary>
/// Saves the state of the current tumbling session.
/// </summary>
void Save();
/// <summary>
/// Loads the saved state of the tumbling execution to the file system.
/// </summary>
/// <returns></returns>
void LoadStateFromMemory();
/// <summary>
/// Deletes the state of the current tumbling session..
/// </summary>
void Delete();
/// <summary>
/// Updates this the state of the current tumbling session.
/// </summary>
void Update();
}
}
using System;
using System.Threading.Tasks;
using NBitcoin;
using NTumbleBit.ClassicTumbler;
namespace Breeze.TumbleBit.Client
{
/// <summary>
/// An interface for managing interactions with the TumbleBit service.
/// </summary>
public interface ITumbleBitManager
{
/// <summary>
/// Connects to the tumbler.
/// </summary>
/// <param name="serverAddress">The URI of the tumbler.</param>
/// <returns></returns>
Task<ClassicTumblerParameters> ConnectToTumblerAsync(Uri serverAddress);
Task TumbleAsync(string originWalletName, string destinationWalletName);
/// <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>
/// Pauses the tumbling.
/// </summary>
void PauseTumbling();
/// <summary>
/// Finishes the tumbling and clean up all saved data.
/// </summary>
void FinishTumbling();
}
}
using System.Threading.Tasks;
using Breeze.TumbleBit.Models;
using NBitcoin;
using NTumbleBit;
using NTumbleBit.ClassicTumbler;
using NTumbleBit.PuzzlePromise;
using NTumbleBit.PuzzleSolver;
using PuzzlePromise = NTumbleBit.PuzzlePromise;
using PuzzleSolver = NTumbleBit.PuzzleSolver;
namespace Breeze.TumbleBit.Client
{
/// <summary>
/// The tumbler service communicating with the tumbler server.
/// </summary>
public interface ITumblerService
{
/// <summary>
/// Gets the tumbler's parameters.
/// </summary>
/// <returns></returns>
Task<ClassicTumblerParameters> GetClassicTumblerParametersAsync();
Task<UnsignedVoucherInformation> AskUnsignedVoucherAsync();
Task<PuzzleSolution> SignVoucherAsync(SignVoucherRequest signVoucherRequest);
Task<ScriptCoin> OpenChannelAsync(OpenChannelRequest request);
Task<TumblerEscrowKeyResponse> RequestTumblerEscrowKeyAsync(int cycleStart);
Task<ServerCommitmentsProof> CheckRevelationAsync(int cycleId, string channelId, PuzzlePromise.ClientRevelation revelation);
Task<SolutionKey[]> CheckRevelationAsync(int cycleId, string channelId, PuzzleSolver.ClientRevelation revelation);
Task<PuzzlePromise.ServerCommitment[]> SignHashesAsync(int cycleId, string channelId, SignaturesRequest sigReq);
Task<OfferInformation> CheckBlindFactorsAsync(int cycleId, string channelId, BlindFactor[] blindFactors);
Task<PuzzleSolver.ServerCommitment[]> SolvePuzzlesAsync(int cycleId, string channelId, PuzzleValue[] puzzles);
Task<SolutionKey[]> FulfillOfferAsync(int cycleId, string channelId, TransactionSignature clientSignature);
Task GiveEscapeKeyAsync(int cycleId, string channelId, TransactionSignature signature);
}
}
using System;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace Breeze.TumbleBit.Models
{
/// <summary>
/// Base class for request objects received to the controllers
/// </summary>
public class RequestModel
{
public override string ToString()
{
return JsonConvert.SerializeObject(this, Formatting.Indented);
}
}
/// <summary>
/// Object used to connect to a tumbler.
/// </summary>
public class TumblerConnectionRequest : RequestModel
{
[Required(ErrorMessage = "A server address is required.")]
public Uri ServerAddress { get; set; }
public string Network { get; set; }
}
public class TumbleRequest
{
[Required(ErrorMessage = "The name of the origin wallet is required.")]
public string OriginWalletName { get; set; }
[Required(ErrorMessage = "The name of the destination wallet is required.")]
public string DestinationWalletName { get; set; }
}
}
using NBitcoin;
using NTumbleBit.ClassicTumbler;
namespace Breeze.TumbleBit.Models
{
public class SignVoucherRequest
{
public int KeyReference { get; set; }
public PubKey TumblerEscrowPubKey { get; set; }
public ClientEscrowInformation ClientEscrowInformation { get; set; }
public MerkleBlock MerkleProof { get; set; }
public Transaction Transaction { get; set; }
}
}
using NBitcoin;
namespace Breeze.TumbleBit.Models
{
public class TumblerEscrowKeyResponse
{
public int KeyIndex { get; set; }
public PubKey PubKey { get; set; }
}
}
namespace Breeze.TumbleBit.Client
{
public enum TransactionType : int
{
TumblerEscrow,
TumblerRedeem,
/// <summary>
/// The transaction that cashout tumbler's escrow (go to client)
/// </summary>
TumblerCashout,
ClientEscrow,
ClientRedeem,
ClientOffer,
ClientEscape,
/// <summary>
/// The transaction that cashout client's escrow (go to tumbler)
/// </summary>
ClientFulfill,
ClientOfferRedeem
}
}
using System;
using System.Collections.Generic;
using Breeze.TumbleBit.Client;
using Breeze.TumbleBit.Controllers;
using Stratis.Bitcoin.Builder.Feature;
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.Builder;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Stratis.Bitcoin.Features.Wallet.JsonConverters;
namespace Breeze.TumbleBit
{
public class TumbleBitFeature : FullNodeFeature
{
public override void Start()
{
}
public override void Stop()
{
}
}
public static class TumbleBitFeatureExtension
{
public static IFullNodeBuilder UseTumbleBit(this IFullNodeBuilder fullNodeBuilder, Uri serverAddress)
{
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<TumbleBitFeature>()
.FeatureServices(services =>
{
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new NetworkConverter() }
};
services.AddSingleton<ITumbleBitManager, TumbleBitManager> ();
services.AddSingleton<TumbleBitController>();
});
});
return fullNodeBuilder;
}
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NTumbleBit.ClassicTumbler;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.WatchOnlyWallet;
using Stratis.Bitcoin.Signals;
namespace Breeze.TumbleBit.Client
{
/// <summary>
/// An implementation of a tumbler manager.
/// </summary>
/// <seealso cref="Breeze.TumbleBit.Client.ITumbleBitManager" />
public class TumbleBitManager : ITumbleBitManager
{
private ITumblerService tumblerService;
private readonly IWalletManager walletManager;
private readonly IWatchOnlyWalletManager watchOnlyWalletManager;
private readonly ILogger logger;
private readonly Signals signals;
private readonly ConcurrentChain chain;
private readonly Network network;
private TumblingState tumblingState;
private IDisposable blockReceiver;
private ClassicTumblerParameters TumblerParameters { get; set; }
public TumbleBitManager(ILoggerFactory loggerFactory, IWalletManager walletManager, IWatchOnlyWalletManager watchOnlyWalletManager, ConcurrentChain chain, Network network, Signals signals)
{
this.walletManager = walletManager;
this.watchOnlyWalletManager = watchOnlyWalletManager;
this.chain = chain;
this.signals = signals;
this.network = network;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
this.tumblingState = new TumblingState(loggerFactory, this.chain, this.walletManager, this.watchOnlyWalletManager, this.network);
}
/// <inheritdoc />
public async Task<ClassicTumblerParameters> ConnectToTumblerAsync(Uri serverAddress)
{
this.tumblerService = new TumblerService(serverAddress);
this.TumblerParameters = await this.tumblerService.GetClassicTumblerParametersAsync();
if (this.TumblerParameters.Network != this.network)
{
throw new Exception($"The tumbler is on network {this.TumblerParameters.Network} while the wallet is on network {this.network}.");
}
// load the current tumbling state fromt he file system
this.tumblingState.LoadStateFromMemory();
// update and save the state
this.tumblingState.TumblerUri = serverAddress;
this.tumblingState.TumblerParameters = this.TumblerParameters;
this.tumblingState.SetClients(this.tumblerService);
this.tumblingState.Save();
return this.TumblerParameters;
}
/// <inheritdoc />
public Task TumbleAsync(string originWalletName, string destinationWalletName)
{
// make sure the tumbler service is initialized
if (this.TumblerParameters == null || this.tumblerService == null)
{
throw new Exception("Please connect to the tumbler first.");
}
// make sure that the user is not trying to resume the process with a different wallet
if (!string.IsNullOrEmpty(this.tumblingState.DestinationWalletName) && this.tumblingState.DestinationWalletName != destinationWalletName)
{
throw new Exception("Please use the same destination wallet until the end of this tumbling session.");
}
Wallet destinationWallet = this.walletManager.GetWallet(destinationWalletName);
if (destinationWallet == null)
{
throw new Exception($"Destination wallet not found. Have you created a wallet with name {destinationWalletName}?");
}
Wallet originWallet = this.walletManager.GetWallet(originWalletName);
if (originWallet == null)
{
throw new Exception($"Origin wallet not found. Have you created a wallet with name {originWalletName}?");
}
// update the state and save
this.tumblingState.DestinationWallet = destinationWallet;
this.tumblingState.DestinationWalletName = destinationWalletName;
this.tumblingState.OriginWallet = originWallet;
this.tumblingState.OriginWalletName = originWalletName;
this.tumblingState.Save();
// subscribe to receiving blocks
this.blockReceiver = this.signals.Blocks.Subscribe(new BlockObserver(this.chain, this));
return Task.CompletedTask;
}
/// <inheritdoc />
public void PauseTumbling()
{
this.logger.LogDebug($"Stopping the tumbling. Current height is {this.chain.Tip.Height}.");
this.blockReceiver.Dispose();
this.tumblingState.Save();
}
/// <inheritdoc />
public void FinishTumbling()
{
this.logger.LogDebug($"The tumbling process is wrapping up. Current height is {this.chain.Tip.Height}.");
this.blockReceiver.Dispose();
this.tumblingState.Delete();
this.tumblingState = null;
}
/// <inheritdoc />
public void ProcessBlock(int height, Block block)
{
this.logger.LogDebug($"Received block with height {height} during tumbling session.");
// update the block height in the tumbling state
this.tumblingState.LastBlockReceivedHeight = height;
this.tumblingState.Save();
// update the state of the tumbling session in this new block
this.tumblingState.Update();
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Breeze.TumbleBit.Models;
using Flurl;
using Flurl.Http;
using Flurl.Http.Configuration;
using NBitcoin;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using NTumbleBit;
using NTumbleBit.ClassicTumbler;
using NTumbleBit.JsonConverters;
using NTumbleBit.PuzzlePromise;
using NTumbleBit.PuzzleSolver;
using PuzzlePromise = NTumbleBit.PuzzlePromise;
using PuzzleSolver = NTumbleBit.PuzzleSolver;
namespace Breeze.TumbleBit.Client
{
public class TumblerService : ITumblerService
{
private readonly string serverAddress;
public TumblerService(Uri serverAddress)
{
this.serverAddress = serverAddress.ToString();
FlurlHttp.Configure(c => {
c.JsonSerializer = new NewtonsoftJsonSerializer(new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new NetworkJsonConverter(), new RsaKeyJsonConverter(), new UInt256JsonConverter() }
});
});
}
/// <inheritdoc />
public async Task<ClassicTumblerParameters> GetClassicTumblerParametersAsync()
{
ClassicTumblerParameters result = await this.serverAddress.AppendPathSegment("/api/v1/tumblers/0/parameters").GetJsonAsync<ClassicTumblerParameters>();
return result;
}
/// <inheritdoc />
public async Task<UnsignedVoucherInformation> AskUnsignedVoucherAsync()
{
UnsignedVoucherInformation result = await this.serverAddress.AppendPathSegment("api/v1/tumblers/0/vouchers/").GetJsonAsync<UnsignedVoucherInformation>();
return result;
}
/// <inheritdoc />
public async Task<PuzzleSolution> SignVoucherAsync(SignVoucherRequest request)
{
PuzzleSolution result = await this.serverAddress.AppendPathSegment("api/v1/tumblers/0/clientchannels/confirm").PostJsonAsync(request).ReceiveJson<PuzzleSolution>();
return result;
}
/// <inheritdoc />
public async Task<ScriptCoin> OpenChannelAsync(OpenChannelRequest request)
{
ScriptCoin result = await this.serverAddress.AppendPathSegment("api/v1/tumblers/0/channels/").PostJsonAsync(request).ReceiveJson<ScriptCoin>();
return result;
}
/// <inheritdoc />
public async Task<TumblerEscrowKeyResponse> RequestTumblerEscrowKeyAsync(int cycleStart)
{
TumblerEscrowKeyResponse result = await this.serverAddress.AppendPathSegment("api/v1/tumblers/0/clientchannels/").PostJsonAsync(cycleStart).ReceiveJson<TumblerEscrowKeyResponse>();
return result;
}
/// <inheritdoc />
public async Task<ServerCommitmentsProof> CheckRevelationAsync(int cycleId, string channelId, PuzzlePromise.ClientRevelation revelation)
{
ServerCommitmentsProof result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/channels/{cycleId}/{channelId}/checkrevelation").PostJsonAsync(revelation).ReceiveJson<ServerCommitmentsProof>();
return result;
}
/// <inheritdoc />
public async Task<SolutionKey[]> CheckRevelationAsync(int cycleId, string channelId, PuzzleSolver.ClientRevelation revelation)
{
SolutionKey[] result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/clientschannels/{cycleId}/{channelId}/checkrevelation").PostJsonAsync(revelation).ReceiveJson<SolutionKey[]>();
return result;
}
/// <inheritdoc />
public async Task<PuzzlePromise.ServerCommitment[]> SignHashesAsync(int cycleId, string channelId, SignaturesRequest sigReq)
{
PuzzlePromise.ServerCommitment[] result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/channels/{cycleId}/{channelId}/signhashes").PostJsonAsync(sigReq).ReceiveJson<PuzzlePromise.ServerCommitment[]>();
return result;
}
/// <inheritdoc />
public async Task<OfferInformation> CheckBlindFactorsAsync(int cycleId, string channelId, BlindFactor[] blindFactors)
{
OfferInformation result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/clientschannels/{cycleId}/{channelId}/checkblindfactors").PostJsonAsync(blindFactors).ReceiveJson<OfferInformation>();
return result;
}
/// <inheritdoc />
public async Task<PuzzleSolver.ServerCommitment[]> SolvePuzzlesAsync(int cycleId, string channelId, PuzzleValue[] puzzles)
{
PuzzleSolver.ServerCommitment[] result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/clientchannels/{cycleId}/{channelId}/solvepuzzles").PostJsonAsync(puzzles).ReceiveJson<PuzzleSolver.ServerCommitment[]>();
return result;
}
/// <inheritdoc />
public async Task<SolutionKey[]> FulfillOfferAsync(int cycleId, string channelId, TransactionSignature clientSignature)
{
SolutionKey[] result = await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/clientchannels/{cycleId}/{channelId}/offer").PostJsonAsync(clientSignature).ReceiveJson<SolutionKey[]>();
return result;
}
/// <inheritdoc />
public async Task GiveEscapeKeyAsync(int cycleId, string channelId, TransactionSignature signature)
{
await this.serverAddress.AppendPathSegment($"api/v1/tumblers/0/clientchannels/{cycleId}/{channelId}/escape").PostJsonAsync(signature);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Newtonsoft.Json;
using NTumbleBit.ClassicTumbler;
using NTumbleBit.PuzzlePromise;
using NTumbleBit.PuzzleSolver;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.WatchOnlyWallet;
namespace Breeze.TumbleBit.Client
{
public partial class TumblingState : IStateMachine
{
private const string StateFileName = "tumblebit_state.json";
private readonly ILogger logger;
private readonly ConcurrentChain chain;
private readonly IWalletManager walletManager;
private readonly IWatchOnlyWalletManager watchOnlyWalletManager;
private readonly CoinType coinType;
[JsonProperty("tumblerParameters")]
public ClassicTumblerParameters TumblerParameters { get; set; }
[JsonProperty("tumblerUri")]
public Uri TumblerUri { get; set; }
[JsonProperty("lastBlockReceivedHeight", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int LastBlockReceivedHeight { get; set; }
[JsonProperty("originWalletName", NullValueHandling = NullValueHandling.Ignore)]
public string OriginWalletName { get; set; }
[JsonProperty("destinationWalletName", NullValueHandling = NullValueHandling.Ignore)]
public string DestinationWalletName { get; set; }
[JsonProperty("sessions", NullValueHandling = NullValueHandling.Ignore)]
public IList<Session> Sessions { get; set; }
[JsonIgnore]
public Wallet OriginWallet { get; set; }
[JsonIgnore]
public Wallet DestinationWallet { get; set; }
[JsonIgnore]
public ITumblerService AliceClient { get; set; }
[JsonIgnore]
public ITumblerService BobClient { get; set; }
[JsonConstructor]
public TumblingState()
{
}
public TumblingState(ILoggerFactory loggerFactory,
ConcurrentChain chain,
IWalletManager walletManager,
IWatchOnlyWalletManager watchOnlyWalletManager,
Network network)
{
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
this.chain = chain;
this.walletManager = walletManager;
this.watchOnlyWalletManager = watchOnlyWalletManager;
this.coinType = (CoinType)network.Consensus.CoinType;
}
public void SetClients(ITumblerService tumblerService)
{
this.AliceClient = tumblerService;
this.BobClient = tumblerService;
}
/// <inheritdoc />
public void Save()
{
File.WriteAllText(GetStateFilePath(), JsonConvert.SerializeObject(this));
}
/// <inheritdoc />
public void LoadStateFromMemory()
{
var stateFilePath = GetStateFilePath();
if (!File.Exists(stateFilePath))
{
return;
}
// load the file from the local system
var savedState = JsonConvert.DeserializeObject<TumblingState>(File.ReadAllText(stateFilePath));
this.Sessions = savedState.Sessions ?? new List<Session>();
this.OriginWalletName = savedState.OriginWalletName;
this.DestinationWalletName = savedState.DestinationWalletName;
this.LastBlockReceivedHeight = savedState.LastBlockReceivedHeight;
this.TumblerParameters = savedState.TumblerParameters;
this.TumblerUri = savedState.TumblerUri;
}
/// <inheritdoc />
public void Delete()
{
var stateFilePath = GetStateFilePath();
File.Delete(stateFilePath);
}
/// <inheritdoc />
public void Update()
{
// get the next cycle to be started
var cycle = this.TumblerParameters.CycleGenerator.GetRegistratingCycle(this.LastBlockReceivedHeight);
// create a new session if allowed
if (this.Sessions.Count == 0)
{
this.CreateNewSession(cycle.Start);
}
else
{
// TODO remove the limitation to have only 1 session
//var lastCycleStarted = this.Sessions.Max(s => s.StartCycle);
//// check if we need to start a new session starting from the registration cycle
//if (lastCycleStarted != cycle.Start)
//{
// if (this.Sessions.SingleOrDefault(s => s.StartCycle == cycle.Start) == null)
// {
// this.CreateNewSession(cycle.Start);
// }
//}
}
// get a list of cycles we expect to have at this height
var cycles = this.TumblerParameters.CycleGenerator.GetCycles(this.LastBlockReceivedHeight);
var existingSessions = cycles.SelectMany(c => this.Sessions.Where(s => s.StartCycle == c.Start)).ToList();
foreach (var existingSession in existingSessions)
{
// create a new session to be updated
var session = new Session();
if (existingSession.NegotiationClientState != null)
{
session.StartCycle = existingSession.NegotiationClientState.CycleStart;
session.ClientChannelNegotiation = new ClientChannelNegotiation(this.TumblerParameters, existingSession.NegotiationClientState);
}
if (existingSession.PromiseClientState != null)
session.PromiseClientSession = new PromiseClientSession(this.TumblerParameters.CreatePromiseParamaters(), existingSession.PromiseClientState);
if (existingSession.SolverClientState != null)
session.SolverClientSession = new SolverClientSession(this.TumblerParameters.CreateSolverParamaters(), existingSession.SolverClientState);
// update the session
this.MoveToNextPhase(session);
// replace the updated session in the list of existing sessions
int index = this.Sessions.IndexOf(existingSession);
if (index != -1)
{
this.Sessions[index] = session;
}
this.Save();
}
}
public void MoveToNextPhase(Session session)
{
this.logger.LogInformation($"Entering next phase for cycle {session.StartCycle}.");
}
public void CreateNewSession(int start)
{
this.Sessions.Add(new Session { StartCycle = start });
this.Save();
}
/// <summary>
/// Gets the file path of the file containing the state of the tumbling execution.
/// </summary>
/// <returns></returns>
private static string GetStateFilePath()
{
string defaultFolderPath;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
defaultFolderPath = $@"{Environment.GetEnvironmentVariable("AppData")}\Breeze\TumbleBit";
}
else
{
defaultFolderPath = $"{Environment.GetEnvironmentVariable("HOME")}/.breeze/TumbleBit";
}
// create the directory if it doesn't exist
Directory.CreateDirectory(defaultFolderPath);
return Path.Combine(defaultFolderPath, StateFileName);
}
}
public class Session
{
public int StartCycle { get; set; }
public ClientChannelNegotiation.State NegotiationClientState { get; set; }
public PromiseClientSession.State PromiseClientState { get; set; }
public SolverClientSession.State SolverClientState { get; set; }
[JsonIgnore]
public ClientChannelNegotiation ClientChannelNegotiation { get; set; }
[JsonIgnore]
public SolverClientSession SolverClientSession { get; set; }
[JsonIgnore]
public PromiseClientSession PromiseClientSession { get; set; }
}
}
<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;
}
}
}
This diff is collapsed.
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;
}
}
}
using NTumbleBit.BouncyCastle.Math;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NTumbleBit
{
public class BlindFactor
{
public BlindFactor(byte[] v)
{
if(v == null)
throw new ArgumentNullException(nameof(v));
_Value = new BigInteger(1, v);
}
internal BlindFactor(BigInteger v)
{
if(v == null)
throw new ArgumentNullException(nameof(v));
_Value = v;
}
internal BigInteger _Value;
public byte[] ToBytes()
{
return _Value.ToByteArrayUnsigned();
}
}
}
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class Asn1Generator
{
private Stream _out;
protected Asn1Generator(
Stream outStream)
{
_out = outStream;
}
protected Stream Out
{
get
{
return _out;
}
}
public abstract void AddObject(Asn1Encodable obj);
public abstract Stream GetRawOutputStream();
public abstract void Close();
}
}
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal interface Asn1OctetStringParser
: IAsn1Convertible
{
Stream GetOctetStream();
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
internal interface Asn1SequenceParser
: IAsn1Convertible
{
IAsn1Convertible ReadObject();
}
}
using System;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
/**
* ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
* a [n] where n is some number - these are assumed to follow the construction
* rules (as with sequences).
*/
internal abstract class Asn1TaggedObject
: Asn1Object
{
internal int tagNo;
// internal bool empty;
internal bool explicitly;
internal Asn1Encodable obj;
public static Asn1TaggedObject GetInstance(
Asn1TaggedObject obj,
bool explicitly)
{
if(explicitly)
{
return (Asn1TaggedObject)obj.GetObject();
}
throw new ArgumentException("implicitly tagged tagged object");
}
public static Asn1TaggedObject GetInstance(
object obj)
{
if(obj == null || obj is Asn1TaggedObject)
{
return (Asn1TaggedObject)obj;
}
throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, nameof(obj));
}
/**
* @param tagNo the tag number for this object.
* @param obj the tagged object.
*/
protected Asn1TaggedObject(
int tagNo,
Asn1Encodable obj)
{
explicitly = true;
this.tagNo = tagNo;
this.obj = obj;
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
Asn1TaggedObject other = asn1Object as Asn1TaggedObject;
if(other == null)
return false;
return tagNo == other.tagNo
// && this.empty == other.empty
&& explicitly == other.explicitly // TODO Should this be part of equality?
&& Platform.Equals(GetObject(), other.GetObject());
}
protected override int Asn1GetHashCode()
{
int code = tagNo.GetHashCode();
// TODO: actually this is wrong - the problem is that a re-encoded
// object may end up with a different hashCode due to implicit
// tagging. As implicit tagging is ambiguous if a sequence is involved
// it seems the only correct method for both equals and hashCode is to
// compare the encodings...
// code ^= explicitly.GetHashCode();
if(obj != null)
{
code ^= obj.GetHashCode();
}
return code;
}
public int TagNo
{
get
{
return tagNo;
}
}
/**
* return whether or not the object may be explicitly tagged.
* <p>
* Note: if the object has been read from an input stream, the only
* time you can be sure if isExplicit is returning the true state of
* affairs is if it returns false. An implicitly tagged object may appear
* to be explicitly tagged, so you need to understand the context under
* which the reading was done as well, see GetObject below.</p>
*/
public bool IsExplicit()
{
return explicitly;
}
public bool IsEmpty()
{
return false; //empty;
}
/**
* return whatever was following the tag.
* <p>
* Note: tagged objects are generally context dependent if you're
* trying to extract a tagged object you should be going via the
* appropriate GetInstance method.</p>
*/
public Asn1Object GetObject()
{
if(obj != null)
{
return obj.ToAsn1Object();
}
return null;
}
public override string ToString()
{
return "[" + tagNo + "]" + obj;
}
}
}
\ No newline at end of file
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class Asn1Encodable
: IAsn1Convertible
{
public const string Der = "DER";
public const string Ber = "BER";
public sealed override int GetHashCode()
{
return ToAsn1Object().CallAsn1GetHashCode();
}
public sealed override bool Equals(
object obj)
{
if(obj == this)
return true;
IAsn1Convertible other = obj as IAsn1Convertible;
if(other == null)
return false;
Asn1Object o1 = ToAsn1Object();
Asn1Object o2 = other.ToAsn1Object();
return o1 == o2 || o1.CallAsn1Equals(o2);
}
public abstract Asn1Object ToAsn1Object();
}
}
using System;
using System.Collections;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class Asn1EncodableVector
: IEnumerable
{
private IList v = Platform.CreateArrayList();
public static Asn1EncodableVector FromEnumerable(
IEnumerable e)
{
Asn1EncodableVector v = new Asn1EncodableVector();
foreach(Asn1Encodable obj in e)
{
v.Add(obj);
}
return v;
}
// public Asn1EncodableVector()
// {
// }
public Asn1EncodableVector(
params Asn1Encodable[] v)
{
Add(v);
}
// public void Add(
// Asn1Encodable obj)
// {
// v.Add(obj);
// }
public void Add(
params Asn1Encodable[] objs)
{
foreach(Asn1Encodable obj in objs)
{
v.Add(obj);
}
}
public void AddOptional(
params Asn1Encodable[] objs)
{
if(objs != null)
{
foreach(Asn1Encodable obj in objs)
{
if(obj != null)
{
v.Add(obj);
}
}
}
}
public Asn1Encodable this[
int index]
{
get
{
return (Asn1Encodable)v[index];
}
}
[Obsolete("Use 'object[index]' syntax instead")]
public Asn1Encodable Get(
int index)
{
return this[index];
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get
{
return v.Count;
}
}
public int Count
{
get
{
return v.Count;
}
}
public IEnumerator GetEnumerator()
{
return v.GetEnumerator();
}
}
}
using System;
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class Asn1Exception
: IOException
{
public Asn1Exception()
: base()
{
}
public Asn1Exception(
string message)
: base(message)
{
}
public Asn1Exception(
string message,
Exception exception)
: base(message, exception)
{
}
}
}
using System;
using System.IO;
using NTumbleBit.BouncyCastle.Utilities.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
/**
* a general purpose ASN.1 decoder - note: this class differs from the
* others in that it returns null after it has read the last object in
* the stream. If an ASN.1 Null is encountered a Der/BER Null object is
* returned.
*/
internal class Asn1InputStream
: FilterStream
{
private readonly int limit;
private readonly byte[][] tmpBuffers;
internal static int FindLimit(Stream input)
{
if(input is LimitedInputStream)
{
return ((LimitedInputStream)input).GetRemaining();
}
else if(input is MemoryStream)
{
MemoryStream mem = (MemoryStream)input;
return (int)(mem.Length - mem.Position);
}
return int.MaxValue;
}
public Asn1InputStream(
Stream inputStream)
: this(inputStream, FindLimit(inputStream))
{
}
/**
* Create an ASN1InputStream where no DER object will be longer than limit.
*
* @param input stream containing ASN.1 encoded data.
* @param limit maximum size of a DER encoded object.
*/
public Asn1InputStream(
Stream inputStream,
int limit)
: base(inputStream)
{
this.limit = limit;
tmpBuffers = new byte[16][];
}
/**
* Create an ASN1InputStream based on the input byte array. The length of DER objects in
* the stream is automatically limited to the length of the input array.
*
* @param input array containing ASN.1 encoded data.
*/
public Asn1InputStream(
byte[] input)
: this(new MemoryStream(input, false), input.Length)
{
}
/**
* build an object given its tag and the number of bytes to construct it from.
*/
private Asn1Object BuildObject(
int tag,
int tagNo,
int length)
{
bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(s, length);
if((tag & Asn1Tags.Application) != 0)
{
throw new IOException("invalid ECDSA sig");
}
if((tag & Asn1Tags.Tagged) != 0)
{
throw new IOException("invalid ECDSA sig");
}
if(isConstructed)
{
switch(tagNo)
{
case Asn1Tags.Sequence:
return CreateDerSequence(defIn);
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}
}
return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
}
internal Asn1EncodableVector BuildEncodableVector()
{
Asn1EncodableVector v = new Asn1EncodableVector();
Asn1Object o;
while((o = ReadObject()) != null)
{
v.Add(o);
}
return v;
}
internal virtual Asn1EncodableVector BuildDerEncodableVector(
DefiniteLengthInputStream dIn)
{
return new Asn1InputStream(dIn).BuildEncodableVector();
}
internal virtual DerSequence CreateDerSequence(
DefiniteLengthInputStream dIn)
{
return DerSequence.FromVector(BuildDerEncodableVector(dIn));
}
public Asn1Object ReadObject()
{
int tag = ReadByte();
if(tag <= 0)
{
if(tag == 0)
throw new IOException("unexpected end-of-contents marker");
return null;
}
//
// calculate tag number
//
int tagNo = ReadTagNumber(s, tag);
//
// calculate length
//
int length = ReadLength(s, limit);
if(length < 0) // indefinite length method
{
throw new IOException("indefinite length primitive encoding encountered");
}
else
{
try
{
return BuildObject(tag, tagNo, length);
}
catch(ArgumentException e)
{
throw new Asn1Exception("corrupted stream detected", e);
}
}
}
internal static int ReadTagNumber(
Stream s,
int tag)
{
int tagNo = tag & 0x1f;
//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if(tagNo == 0x1f)
{
tagNo = 0;
int b = s.ReadByte();
// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if((b & 0x7f) == 0) // Note: -1 will pass
{
throw new IOException("Corrupted stream - invalid high tag number found");
}
while((b >= 0) && ((b & 0x80) != 0))
{
tagNo |= (b & 0x7f);
tagNo <<= 7;
b = s.ReadByte();
}
if(b < 0)
throw new EndOfStreamException("EOF found inside tag value.");
tagNo |= (b & 0x7f);
}
return tagNo;
}
internal static int ReadLength(
Stream s,
int limit)
{
int length = s.ReadByte();
if(length < 0)
throw new EndOfStreamException("EOF found when length expected");
if(length == 0x80)
return -1; // indefinite-length encoding
if(length > 127)
{
int size = length & 0x7f;
// Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
if(size > 4)
throw new IOException("DER length more than 4 bytes: " + size);
length = 0;
for(int i = 0; i < size; i++)
{
int next = s.ReadByte();
if(next < 0)
throw new EndOfStreamException("EOF found reading length");
length = (length << 8) + next;
}
if(length < 0)
throw new IOException("Corrupted stream - negative length found");
if(length >= limit) // after all we must have read at least 1 byte
throw new IOException("Corrupted stream - out of bounds length found");
}
return length;
}
internal static byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
{
int len = defIn.GetRemaining();
if(len >= tmpBuffers.Length)
{
return defIn.ToArray();
}
byte[] buf = tmpBuffers[len];
if(buf == null)
{
buf = tmpBuffers[len] = new byte[len];
}
defIn.ReadAllIntoByteArray(buf);
return buf;
}
internal static Asn1Object CreatePrimitiveDerObject(
int tagNo,
DefiniteLengthInputStream defIn,
byte[][] tmpBuffers)
{
switch(tagNo)
{
case Asn1Tags.Boolean:
throw new IOException("invalid ECDSA sig");
case Asn1Tags.Enumerated:
throw new IOException("invalid ECDSA sig");
case Asn1Tags.ObjectIdentifier:
return DerObjectIdentifier.FromOctetString(GetBuffer(defIn, tmpBuffers));
}
byte[] bytes = defIn.ToArray();
switch(tagNo)
{
case Asn1Tags.Integer:
return new DerInteger(bytes);
case Asn1Tags.OctetString:
return new DerOctetString(bytes);
case Asn1Tags.Null:
return DerNull.Instance; // actual content is ignored (enforce 0 length?)
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}
}
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
/**
* A Null object.
*/
internal abstract class Asn1Null
: Asn1Object
{
internal Asn1Null()
{
}
public override string ToString()
{
return "NULL";
}
}
}
using System;
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class Asn1Object
: Asn1Encodable
{
/// <summary>Create a base ASN.1 object from a byte array.</summary>
/// <param name="data">The byte array to parse.</param>
/// <returns>The base ASN.1 object represented by the byte array.</returns>
/// <exception cref="IOException">If there is a problem parsing the data.</exception>
public static Asn1Object FromByteArray(
byte[] data)
{
try
{
MemoryStream input = new MemoryStream(data, false);
Asn1InputStream asn1 = new Asn1InputStream(input, data.Length);
Asn1Object result = asn1.ReadObject();
if(input.Position != input.Length)
throw new IOException("extra data found after object");
return result;
}
catch(InvalidCastException)
{
throw new IOException("cannot recognise object in byte array");
}
}
/// <summary>Read a base ASN.1 object from a stream.</summary>
/// <param name="inStr">The stream to parse.</param>
/// <returns>The base ASN.1 object represented by the byte array.</returns>
/// <exception cref="IOException">If there is a problem parsing the data.</exception>
public static Asn1Object FromStream(
Stream inStr)
{
try
{
return new Asn1InputStream(inStr).ReadObject();
}
catch(InvalidCastException)
{
throw new IOException("cannot recognise object in stream");
}
}
public sealed override Asn1Object ToAsn1Object()
{
return this;
}
internal abstract void Encode(DerOutputStream derOut);
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Asn1OutputStream aOut = new Asn1OutputStream(bOut);
aOut.WriteObject(this);
return bOut.ToArray();
}
protected abstract bool Asn1Equals(Asn1Object asn1Object);
protected abstract int Asn1GetHashCode();
internal bool CallAsn1Equals(Asn1Object obj)
{
return Asn1Equals(obj);
}
internal int CallAsn1GetHashCode()
{
return Asn1GetHashCode();
}
}
}
using System;
using System.IO;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class Asn1OctetString
: Asn1Object, Asn1OctetStringParser
{
internal byte[] str;
/**
* @param string the octets making up the octet string.
*/
internal Asn1OctetString(
byte[] str)
{
if(str == null)
throw new ArgumentNullException(nameof(str));
this.str = str;
}
public Stream GetOctetStream()
{
return new MemoryStream(str, false);
}
public Asn1OctetStringParser Parser
{
get
{
return this;
}
}
public virtual byte[] GetOctets()
{
return str;
}
protected override int Asn1GetHashCode()
{
return Arrays.GetHashCode(GetOctets());
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
DerOctetString other = asn1Object as DerOctetString;
if(other == null)
return false;
return Arrays.AreEqual(GetOctets(), other.GetOctets());
}
}
}
using System;
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class Asn1OutputStream
: DerOutputStream
{
public Asn1OutputStream(Stream os) : base(os)
{
}
[Obsolete("Use version taking an Asn1Encodable arg instead")]
public override void WriteObject(
object obj)
{
if(obj == null)
{
WriteNull();
}
else if(obj is Asn1Object)
{
((Asn1Object)obj).Encode(this);
}
else if(obj is Asn1Encodable)
{
((Asn1Encodable)obj).ToAsn1Object().Encode(this);
}
else
{
throw new IOException("object not Asn1Encodable");
}
}
}
}
\ No newline at end of file
using System;
using System.Collections;
using System.IO;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class Asn1Sequence
: Asn1Object, IEnumerable
{
private readonly IList seq;
/**
* return an Asn1Sequence from the given object.
*
* @param obj the object we want converted.
* @exception ArgumentException if the object cannot be converted.
*/
public static Asn1Sequence GetInstance(
object obj)
{
if(obj == null || obj is Asn1Sequence)
{
return (Asn1Sequence)obj;
}
else if(obj is Asn1SequenceParser)
{
return GetInstance(((Asn1SequenceParser)obj).ToAsn1Object());
}
else if(obj is byte[])
{
try
{
return GetInstance(FromByteArray((byte[])obj));
}
catch(IOException e)
{
throw new ArgumentException("failed to construct sequence from byte[]: " + e.Message);
}
}
else if(obj is Asn1Encodable)
{
Asn1Object primitive = ((Asn1Encodable)obj).ToAsn1Object();
if(primitive is Asn1Sequence)
{
return (Asn1Sequence)primitive;
}
}
throw new ArgumentException("Unknown object in GetInstance: " + Platform.GetTypeName(obj), nameof(obj));
}
protected internal Asn1Sequence(
int capacity)
{
seq = Platform.CreateArrayList(capacity);
}
public virtual IEnumerator GetEnumerator()
{
return seq.GetEnumerator();
}
[Obsolete("Use GetEnumerator() instead")]
public IEnumerator GetObjects()
{
return GetEnumerator();
}
private class Asn1SequenceParserImpl
: Asn1SequenceParser
{
private readonly Asn1Sequence outer;
private readonly int max;
private int index;
public Asn1SequenceParserImpl(
Asn1Sequence outer)
{
this.outer = outer;
max = outer.Count;
}
public IAsn1Convertible ReadObject()
{
if(index == max)
return null;
Asn1Encodable obj = outer[index++];
if(obj is Asn1Sequence)
return ((Asn1Sequence)obj).Parser;
// NB: Asn1OctetString implements Asn1OctetStringParser directly
// if (obj is Asn1OctetString)
// return ((Asn1OctetString)obj).Parser;
return obj;
}
public Asn1Object ToAsn1Object()
{
return outer;
}
}
public virtual Asn1SequenceParser Parser
{
get
{
return new Asn1SequenceParserImpl(this);
}
}
/**
* return the object at the sequence position indicated by index.
*
* @param index the sequence number (starting at zero) of the object
* @return the object at the sequence position indicated by index.
*/
public virtual Asn1Encodable this[int index]
{
get
{
return (Asn1Encodable)seq[index];
}
}
[Obsolete("Use 'object[index]' syntax instead")]
public Asn1Encodable GetObjectAt(
int index)
{
return this[index];
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get
{
return Count;
}
}
public virtual int Count
{
get
{
return seq.Count;
}
}
protected override int Asn1GetHashCode()
{
int hc = Count;
foreach(object o in this)
{
hc *= 17;
if(o == null)
{
hc ^= DerNull.Instance.GetHashCode();
}
else
{
hc ^= o.GetHashCode();
}
}
return hc;
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
Asn1Sequence other = asn1Object as Asn1Sequence;
if(other == null)
return false;
if(Count != other.Count)
return false;
IEnumerator s1 = GetEnumerator();
IEnumerator s2 = other.GetEnumerator();
while(s1.MoveNext() && s2.MoveNext())
{
Asn1Object o1 = GetCurrent(s1).ToAsn1Object();
Asn1Object o2 = GetCurrent(s2).ToAsn1Object();
if(!o1.Equals(o2))
return false;
}
return true;
}
private Asn1Encodable GetCurrent(IEnumerator e)
{
Asn1Encodable encObj = (Asn1Encodable)e.Current;
// unfortunately null was allowed as a substitute for DER null
if(encObj == null)
return DerNull.Instance;
return encObj;
}
protected internal void AddObject(
Asn1Encodable obj)
{
seq.Add(obj);
}
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class Asn1Tags
{
public const int Boolean = 0x01;
public const int Integer = 0x02;
public const int BitString = 0x03;
public const int OctetString = 0x04;
public const int Null = 0x05;
public const int ObjectIdentifier = 0x06;
public const int External = 0x08;
public const int Enumerated = 0x0a;
public const int Sequence = 0x10;
public const int SequenceOf = 0x10; // for completeness
public const int Set = 0x11;
public const int SetOf = 0x11; // for completeness
public const int NumericString = 0x12;
public const int PrintableString = 0x13;
public const int T61String = 0x14;
public const int VideotexString = 0x15;
public const int IA5String = 0x16;
public const int UtcTime = 0x17;
public const int GeneralizedTime = 0x18;
public const int GraphicString = 0x19;
public const int VisibleString = 0x1a;
public const int GeneralString = 0x1b;
public const int UniversalString = 0x1c;
public const int BmpString = 0x1e;
public const int Utf8String = 0x0c;
public const int Constructed = 0x20;
public const int Application = 0x40;
public const int Tagged = 0x80;
}
}
using System.IO;
using NTumbleBit.BouncyCastle.Utilities.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class DerGenerator
: Asn1Generator
{
private bool _tagged = false;
private bool _isExplicit;
private int _tagNo;
protected DerGenerator(
Stream outStream)
: base(outStream)
{
}
protected DerGenerator(
Stream outStream,
int tagNo,
bool isExplicit)
: base(outStream)
{
_tagged = true;
_isExplicit = isExplicit;
_tagNo = tagNo;
}
private static void WriteLength(
Stream outStr,
int length)
{
if(length > 127)
{
int size = 1;
int val = length;
while((val >>= 8) != 0)
{
size++;
}
outStr.WriteByte((byte)(size | 0x80));
for(int i = (size - 1) * 8; i >= 0; i -= 8)
{
outStr.WriteByte((byte)(length >> i));
}
}
else
{
outStr.WriteByte((byte)length);
}
}
internal static void WriteDerEncoded(
Stream outStream,
int tag,
byte[] bytes)
{
outStream.WriteByte((byte)tag);
WriteLength(outStream, bytes.Length);
outStream.Write(bytes, 0, bytes.Length);
}
internal void WriteDerEncoded(
int tag,
byte[] bytes)
{
if(_tagged)
{
int tagNum = _tagNo | Asn1Tags.Tagged;
if(_isExplicit)
{
int newTag = _tagNo | Asn1Tags.Constructed | Asn1Tags.Tagged;
MemoryStream bOut = new MemoryStream();
WriteDerEncoded(bOut, tag, bytes);
WriteDerEncoded(Out, newTag, bOut.ToArray());
}
else
{
if((tag & Asn1Tags.Constructed) != 0)
{
tagNum |= Asn1Tags.Constructed;
}
WriteDerEncoded(Out, tagNum, bytes);
}
}
else
{
WriteDerEncoded(Out, tag, bytes);
}
}
internal static void WriteDerEncoded(
Stream outStr,
int tag,
Stream inStr)
{
WriteDerEncoded(outStr, tag, Streams.ReadAll(inStr));
}
}
}
using System.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerSequenceGenerator
: DerGenerator
{
private readonly MemoryStream _bOut = new MemoryStream();
public DerSequenceGenerator(
Stream outStream)
: base(outStream)
{
}
public DerSequenceGenerator(
Stream outStream,
int tagNo,
bool isExplicit)
: base(outStream, tagNo, isExplicit)
{
}
public override void AddObject(
Asn1Encodable obj)
{
new DerOutputStream(_bOut).WriteObject(obj);
}
public override Stream GetRawOutputStream()
{
return _bOut;
}
public override void Close()
{
WriteDerEncoded(Asn1Tags.Constructed | Asn1Tags.Sequence, _bOut.ToArray());
}
}
}
using System;
using System.IO;
using NTumbleBit.BouncyCastle.Utilities.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DefiniteLengthInputStream
: LimitedInputStream
{
private static readonly byte[] EmptyBytes = new byte[0];
private readonly int _originalLength;
private int _remaining;
internal DefiniteLengthInputStream(
Stream inStream,
int length)
: base(inStream, length)
{
if(length < 0)
throw new ArgumentException("negative lengths not allowed", nameof(length));
_originalLength = length;
_remaining = length;
if(length == 0)
{
SetParentEofDetect(true);
}
}
internal int Remaining
{
get
{
return _remaining;
}
}
public override int ReadByte()
{
if(_remaining == 0)
return -1;
int b = _in.ReadByte();
if(b < 0)
throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
if(--_remaining == 0)
{
SetParentEofDetect(true);
}
return b;
}
public override int Read(
byte[] buf,
int off,
int len)
{
if(_remaining == 0)
return 0;
int toRead = System.Math.Min(len, _remaining);
int numRead = _in.Read(buf, off, toRead);
if(numRead < 1)
throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
if((_remaining -= numRead) == 0)
{
SetParentEofDetect(true);
}
return numRead;
}
internal void ReadAllIntoByteArray(byte[] buf)
{
if(_remaining != buf.Length)
throw new ArgumentException("buffer length not right for data");
if((_remaining -= Streams.ReadFully(_in, buf)) != 0)
throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
SetParentEofDetect(true);
}
internal byte[] ToArray()
{
if(_remaining == 0)
return EmptyBytes;
byte[] bytes = new byte[_remaining];
if((_remaining -= Streams.ReadFully(_in, bytes)) != 0)
throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
SetParentEofDetect(true);
return bytes;
}
}
}
using System;
using NTumbleBit.BouncyCastle.Math;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerInteger
: Asn1Object
{
private readonly byte[] bytes;
/**
* return an integer from the passed in object
*
* @exception ArgumentException if the object cannot be converted.
*/
public static DerInteger GetInstance(
object obj)
{
if(obj == null || obj is DerInteger)
{
return (DerInteger)obj;
}
throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
}
public DerInteger(
int value)
{
bytes = BigInteger.ValueOf(value).ToByteArray();
}
public DerInteger(
BigInteger value)
{
if(value == null)
throw new ArgumentNullException(nameof(value));
bytes = value.ToByteArray();
}
public DerInteger(
byte[] bytes)
{
this.bytes = bytes;
}
public BigInteger Value
{
get
{
return new BigInteger(bytes);
}
}
/**
* in some cases positive values Get crammed into a space,
* that's not quite big enough...
*/
public BigInteger PositiveValue
{
get
{
return new BigInteger(1, bytes);
}
}
internal override void Encode(
DerOutputStream derOut)
{
derOut.WriteEncoded(Asn1Tags.Integer, bytes);
}
protected override int Asn1GetHashCode()
{
return Arrays.GetHashCode(bytes);
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
DerInteger other = asn1Object as DerInteger;
if(other == null)
return false;
return Arrays.AreEqual(bytes, other.bytes);
}
public override string ToString()
{
return Value.ToString();
}
}
}
using System;
namespace NTumbleBit.BouncyCastle.Asn1
{
/**
* A Null object.
*/
internal class DerNull
: Asn1Null
{
public static readonly DerNull Instance = new DerNull(0);
private byte[] zeroBytes = new byte[0];
[Obsolete("Use static Instance object")]
public DerNull()
{
}
protected internal DerNull(int dummy)
{
}
internal override void Encode(
DerOutputStream derOut)
{
derOut.WriteEncoded(Asn1Tags.Null, zeroBytes);
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
return asn1Object is DerNull;
}
protected override int Asn1GetHashCode()
{
return -1;
}
}
}
using System;
using System.IO;
using System.Text;
using NTumbleBit.BouncyCastle.Math;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerObjectIdentifier
: Asn1Object
{
private readonly string identifier;
private byte[] body = null;
public DerObjectIdentifier(
string identifier)
{
if(identifier == null)
throw new ArgumentNullException(nameof(identifier));
if(!IsValidIdentifier(identifier))
throw new FormatException("string " + identifier + " not an OID");
this.identifier = identifier;
}
internal DerObjectIdentifier(DerObjectIdentifier oid, string branchID)
{
if(!IsValidBranchID(branchID, 0))
throw new ArgumentException("string " + branchID + " not a valid OID branch", nameof(branchID));
identifier = oid.Id + "." + branchID;
}
// TODO Change to ID?
public string Id
{
get
{
return identifier;
}
}
public virtual DerObjectIdentifier Branch(string branchID)
{
return new DerObjectIdentifier(this, branchID);
}
/**
* Return true if this oid is an extension of the passed in branch, stem.
* @param stem the arc or branch that is a possible parent.
* @return true if the branch is on the passed in stem, false otherwise.
*/
public virtual bool On(DerObjectIdentifier stem)
{
string id = Id, stemId = stem.Id;
return id.Length > stemId.Length && id[stemId.Length] == '.' && Platform.StartsWith(id, stemId);
}
internal DerObjectIdentifier(byte[] bytes)
{
identifier = MakeOidStringFromBytes(bytes);
body = Arrays.Clone(bytes);
}
private void WriteField(
Stream outputStream,
long fieldValue)
{
byte[] result = new byte[9];
int pos = 8;
result[pos] = (byte)(fieldValue & 0x7f);
while(fieldValue >= (1L << 7))
{
fieldValue >>= 7;
result[--pos] = (byte)((fieldValue & 0x7f) | 0x80);
}
outputStream.Write(result, pos, 9 - pos);
}
private void WriteField(
Stream outputStream,
BigInteger fieldValue)
{
int byteCount = (fieldValue.BitLength + 6) / 7;
if(byteCount == 0)
{
outputStream.WriteByte(0);
}
else
{
BigInteger tmpValue = fieldValue;
byte[] tmp = new byte[byteCount];
for(int i = byteCount - 1; i >= 0; i--)
{
tmp[i] = (byte)((tmpValue.IntValue & 0x7f) | 0x80);
tmpValue = tmpValue.ShiftRight(7);
}
tmp[byteCount - 1] &= 0x7f;
outputStream.Write(tmp, 0, tmp.Length);
}
}
protected override int Asn1GetHashCode()
{
return identifier.GetHashCode();
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
DerObjectIdentifier other = asn1Object as DerObjectIdentifier;
if(other == null)
return false;
return identifier.Equals(other.identifier);
}
public override string ToString()
{
return identifier;
}
private static bool IsValidBranchID(
String branchID, int start)
{
bool periodAllowed = false;
int pos = branchID.Length;
while(--pos >= start)
{
char ch = branchID[pos];
// TODO Leading zeroes?
if('0' <= ch && ch <= '9')
{
periodAllowed = true;
continue;
}
if(ch == '.')
{
if(!periodAllowed)
return false;
periodAllowed = false;
continue;
}
return false;
}
return periodAllowed;
}
private static bool IsValidIdentifier(string identifier)
{
if(identifier.Length < 3 || identifier[1] != '.')
return false;
char first = identifier[0];
if(first < '0' || first > '2')
return false;
return IsValidBranchID(identifier, 2);
}
private const long LONG_LIMIT = (long.MaxValue >> 7) - 0x7f;
private static string MakeOidStringFromBytes(
byte[] bytes)
{
StringBuilder objId = new StringBuilder();
long value = 0;
BigInteger bigValue = null;
bool first = true;
for(int i = 0; i != bytes.Length; i++)
{
int b = bytes[i];
if(value <= LONG_LIMIT)
{
value += (b & 0x7f);
if((b & 0x80) == 0) // end of number reached
{
if(first)
{
if(value < 40)
{
objId.Append('0');
}
else if(value < 80)
{
objId.Append('1');
value -= 40;
}
else
{
objId.Append('2');
value -= 80;
}
first = false;
}
objId.Append('.');
objId.Append(value);
value = 0;
}
else
{
value <<= 7;
}
}
else
{
if(bigValue == null)
{
bigValue = BigInteger.ValueOf(value);
}
bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7f));
if((b & 0x80) == 0)
{
if(first)
{
objId.Append('2');
bigValue = bigValue.Subtract(BigInteger.ValueOf(80));
first = false;
}
objId.Append('.');
objId.Append(bigValue);
bigValue = null;
value = 0;
}
else
{
bigValue = bigValue.ShiftLeft(7);
}
}
}
return objId.ToString();
}
private static readonly DerObjectIdentifier[] cache = new DerObjectIdentifier[1024];
internal byte[] GetBody()
{
lock(this)
{
if(body == null)
{
MemoryStream bOut = new MemoryStream();
DoOutput(bOut);
body = bOut.ToArray();
}
}
return body;
}
private void DoOutput(MemoryStream bOut)
{
OidTokenizer tok = new OidTokenizer(identifier);
string token = tok.NextToken();
int first = int.Parse(token) * 40;
token = tok.NextToken();
if(token.Length <= 18)
{
WriteField(bOut, first + Int64.Parse(token));
}
else
{
WriteField(bOut, new BigInteger(token).Add(BigInteger.ValueOf(first)));
}
while(tok.HasMoreTokens)
{
token = tok.NextToken();
if(token.Length <= 18)
{
WriteField(bOut, Int64.Parse(token));
}
else
{
WriteField(bOut, new BigInteger(token));
}
}
}
internal override void Encode(DerOutputStream derOut)
{
derOut.WriteEncoded(Asn1Tags.ObjectIdentifier, GetBody());
}
internal static Asn1Object FromOctetString(byte[] enc)
{
int hashCode = Arrays.GetHashCode(enc);
int first = hashCode & 1023;
lock(cache)
{
DerObjectIdentifier entry = cache[first];
if(entry != null && Arrays.AreEqual(enc, entry.GetBody()))
{
return entry;
}
return cache[first] = new DerObjectIdentifier(enc);
}
}
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerOctetString
: Asn1OctetString
{
/// <param name="str">The octets making up the octet string.</param>
public DerOctetString(
byte[] str)
: base(str)
{
}
internal override void Encode(
DerOutputStream derOut)
{
derOut.WriteEncoded(Asn1Tags.OctetString, str);
}
internal static void Encode(
DerOutputStream derOut,
byte[] bytes,
int offset,
int length)
{
derOut.WriteEncoded(Asn1Tags.OctetString, bytes, offset, length);
}
}
}
using System;
using System.IO;
using NTumbleBit.BouncyCastle.Utilities.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerOutputStream
: FilterStream
{
public DerOutputStream(Stream os)
: base(os)
{
}
private void WriteLength(
int length)
{
if(length > 127)
{
int size = 1;
uint val = (uint)length;
while((val >>= 8) != 0)
{
size++;
}
WriteByte((byte)(size | 0x80));
for(int i = (size - 1) * 8; i >= 0; i -= 8)
{
WriteByte((byte)(length >> i));
}
}
else
{
WriteByte((byte)length);
}
}
internal void WriteEncoded(
int tag,
byte[] bytes)
{
WriteByte((byte)tag);
WriteLength(bytes.Length);
Write(bytes, 0, bytes.Length);
}
internal void WriteEncoded(
int tag,
byte first,
byte[] bytes)
{
WriteByte((byte)tag);
WriteLength(bytes.Length + 1);
WriteByte(first);
Write(bytes, 0, bytes.Length);
}
internal void WriteEncoded(
int tag,
byte[] bytes,
int offset,
int length)
{
WriteByte((byte)tag);
WriteLength(length);
Write(bytes, offset, length);
}
internal void WriteTag(
int flags,
int tagNo)
{
if(tagNo < 31)
{
WriteByte((byte)(flags | tagNo));
}
else
{
WriteByte((byte)(flags | 0x1f));
if(tagNo < 128)
{
WriteByte((byte)tagNo);
}
else
{
byte[] stack = new byte[5];
int pos = stack.Length;
stack[--pos] = (byte)(tagNo & 0x7F);
do
{
tagNo >>= 7;
stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
}
while(tagNo > 127);
Write(stack, pos, stack.Length - pos);
}
}
}
internal void WriteEncoded(
int flags,
int tagNo,
byte[] bytes)
{
WriteTag(flags, tagNo);
WriteLength(bytes.Length);
Write(bytes, 0, bytes.Length);
}
protected void WriteNull()
{
WriteByte(Asn1Tags.Null);
WriteByte(0x00);
}
[Obsolete("Use version taking an Asn1Encodable arg instead")]
public virtual void WriteObject(
object obj)
{
if(obj == null)
{
WriteNull();
}
else if(obj is Asn1Object)
{
((Asn1Object)obj).Encode(this);
}
else if(obj is Asn1Encodable)
{
((Asn1Encodable)obj).ToAsn1Object().Encode(this);
}
else
{
throw new IOException("object not Asn1Object");
}
}
public virtual void WriteObject(
Asn1Encodable obj)
{
if(obj == null)
{
WriteNull();
}
else
{
obj.ToAsn1Object().Encode(this);
}
}
public virtual void WriteObject(
Asn1Object obj)
{
if(obj == null)
{
WriteNull();
}
else
{
obj.Encode(this);
}
}
}
}
using System.IO;
using NTumbleBit.BouncyCastle.Utilities;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal class DerSequence
: Asn1Sequence
{
public static readonly DerSequence Empty = new DerSequence();
public static DerSequence FromVector(
Asn1EncodableVector v)
{
return v.Count < 1 ? Empty : new DerSequence(v);
}
/**
* create an empty sequence
*/
public DerSequence()
: base(0)
{
}
/**
* create a sequence containing one object
*/
public DerSequence(
Asn1Encodable obj)
: base(1)
{
AddObject(obj);
}
public DerSequence(
params Asn1Encodable[] v)
: base(v.Length)
{
foreach(Asn1Encodable ae in v)
{
AddObject(ae);
}
}
/**
* create a sequence containing a vector of objects.
*/
public DerSequence(
Asn1EncodableVector v)
: base(v.Count)
{
foreach(Asn1Encodable ae in v)
{
AddObject(ae);
}
}
/*
* A note on the implementation:
* <p>
* As Der requires the constructed, definite-length model to
* be used for structured types, this varies slightly from the
* ASN.1 descriptions given. Rather than just outputing Sequence,
* we also have to specify Constructed, and the objects length.
*/
internal override void Encode(
DerOutputStream derOut)
{
// TODO Intermediate buffer could be avoided if we could calculate expected length
MemoryStream bOut = new MemoryStream();
DerOutputStream dOut = new DerOutputStream(bOut);
foreach(Asn1Encodable obj in this)
{
dOut.WriteObject(obj);
}
Platform.Dispose(dOut);
byte[] bytes = bOut.ToArray();
derOut.WriteEncoded(Asn1Tags.Sequence | Asn1Tags.Constructed, bytes);
}
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
internal interface IAsn1Convertible
{
Asn1Object ToAsn1Object();
}
}
using System.IO;
using NTumbleBit.BouncyCastle.Utilities.IO;
namespace NTumbleBit.BouncyCastle.Asn1
{
internal abstract class LimitedInputStream
: BaseInputStream
{
protected readonly Stream _in;
private int _limit;
internal LimitedInputStream(
Stream inStream,
int limit)
{
_in = inStream;
_limit = limit;
}
internal virtual int GetRemaining()
{
// TODO: maybe one day this can become more accurate
return _limit;
}
protected virtual void SetParentEofDetect(bool on)
{
}
}
}
namespace NTumbleBit.BouncyCastle.Asn1
{
/**
* class for breaking up an Oid into it's component tokens, ala
* java.util.StringTokenizer. We need this class as some of the
* lightweight Java environment don't support classes like
* StringTokenizer.
*/
public class OidTokenizer
{
private string oid;
private int index;
public OidTokenizer(
string oid)
{
this.oid = oid;
}
public bool HasMoreTokens
{
get
{
return index != -1;
}
}
public string NextToken()
{
if(index == -1)
{
return null;
}
int end = oid.IndexOf('.', index);
if(end == -1)
{
string lastToken = oid.Substring(index);
index = -1;
return lastToken;
}
string nextToken = oid.Substring(index, end - index);
index = end + 1;
return nextToken;
}
}
}
\ No newline at end of file
using System;
namespace NTumbleBit.BouncyCastle.Asn1.X509
{
internal class AlgorithmIdentifier
: Asn1Encodable
{
private readonly DerObjectIdentifier objectID;
private readonly Asn1Encodable parameters;
private readonly bool parametersDefined;
public AlgorithmIdentifier(
DerObjectIdentifier objectID)
{
this.objectID = objectID;
}
public AlgorithmIdentifier(
string objectID)
{
this.objectID = new DerObjectIdentifier(objectID);
}
public AlgorithmIdentifier(
DerObjectIdentifier objectID,
Asn1Encodable parameters)
{
this.objectID = objectID;
this.parameters = parameters;
parametersDefined = true;
}
public virtual DerObjectIdentifier ObjectID
{
get
{
return objectID;
}
}
public Asn1Encodable Parameters
{
get
{
return parameters;
}
}
/**
* Produce an object suitable for an Asn1OutputStream.
* <pre>
* AlgorithmIdentifier ::= Sequence {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
* </pre>
*/
public override Asn1Object ToAsn1Object()
{
Asn1EncodableVector v = new Asn1EncodableVector(objectID);
if(parametersDefined)
{
if(parameters != null)
{
v.Add(parameters);
}
else
{
v.Add(DerNull.Instance);
}
}
return new DerSequence(v);
}
}
}
\ No newline at end of file
using System;
using System.Collections;
using System.IO;
using NTumbleBit.BouncyCastle.Asn1.X509;
using NTumbleBit.BouncyCastle.Math;
namespace NTumbleBit.BouncyCastle.Asn1.Pkcs
{
internal class PrivateKeyInfo
: Asn1Encodable
{
private readonly Asn1Object privKey;
private readonly AlgorithmIdentifier algID;
public PrivateKeyInfo(
AlgorithmIdentifier algID,
Asn1Object privateKey)
{
privKey = privateKey;
this.algID = algID;
}
public AlgorithmIdentifier AlgorithmID
{
get
{
return algID;
}
}
public Asn1Object PrivateKey
{
get
{
return privKey;
}
}
/**
* write out an RSA private key with its associated information
* as described in Pkcs8.
* <pre>
* PrivateKeyInfo ::= Sequence {
* version Version,
* privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
* privateKey PrivateKey,
* attributes [0] IMPLICIT Attributes OPTIONAL
* }
* Version ::= Integer {v1(0)} (v1,...)
*
* PrivateKey ::= OCTET STRING
*
* Attributes ::= Set OF Attr
* </pre>
*/
public override Asn1Object ToAsn1Object()
{
Asn1EncodableVector v = new Asn1EncodableVector(
new DerInteger(0),
algID,
new DerOctetString(privKey.GetEncoded()));
return new DerSequence(v);
}
}
}
\ No newline at end of file
namespace NTumbleBit.BouncyCastle.Asn1.Sec
{
internal abstract class SecObjectIdentifiers
{
/**
* EllipticCurve OBJECT IDENTIFIER ::= {
* iso(1) identified-organization(3) certicom(132) curve(0)
* }
*/
public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.132.0");
public static readonly DerObjectIdentifier SecT163k1 = new DerObjectIdentifier(EllipticCurve + ".1");
public static readonly DerObjectIdentifier SecT163r1 = new DerObjectIdentifier(EllipticCurve + ".2");
public static readonly DerObjectIdentifier SecT239k1 = new DerObjectIdentifier(EllipticCurve + ".3");
public static readonly DerObjectIdentifier SecT113r1 = new DerObjectIdentifier(EllipticCurve + ".4");
public static readonly DerObjectIdentifier SecT113r2 = new DerObjectIdentifier(EllipticCurve + ".5");
public static readonly DerObjectIdentifier SecP112r1 = new DerObjectIdentifier(EllipticCurve + ".6");
public static readonly DerObjectIdentifier SecP112r2 = new DerObjectIdentifier(EllipticCurve + ".7");
public static readonly DerObjectIdentifier SecP160r1 = new DerObjectIdentifier(EllipticCurve + ".8");
public static readonly DerObjectIdentifier SecP160k1 = new DerObjectIdentifier(EllipticCurve + ".9");
public static readonly DerObjectIdentifier SecP256k1 = new DerObjectIdentifier(EllipticCurve + ".10");
public static readonly DerObjectIdentifier SecT163r2 = new DerObjectIdentifier(EllipticCurve + ".15");
public static readonly DerObjectIdentifier SecT283k1 = new DerObjectIdentifier(EllipticCurve + ".16");
public static readonly DerObjectIdentifier SecT283r1 = new DerObjectIdentifier(EllipticCurve + ".17");
public static readonly DerObjectIdentifier SecT131r1 = new DerObjectIdentifier(EllipticCurve + ".22");
public static readonly DerObjectIdentifier SecT131r2 = new DerObjectIdentifier(EllipticCurve + ".23");
public static readonly DerObjectIdentifier SecT193r1 = new DerObjectIdentifier(EllipticCurve + ".24");
public static readonly DerObjectIdentifier SecT193r2 = new DerObjectIdentifier(EllipticCurve + ".25");
public static readonly DerObjectIdentifier SecT233k1 = new DerObjectIdentifier(EllipticCurve + ".26");
public static readonly DerObjectIdentifier SecT233r1 = new DerObjectIdentifier(EllipticCurve + ".27");
public static readonly DerObjectIdentifier SecP128r1 = new DerObjectIdentifier(EllipticCurve + ".28");
public static readonly DerObjectIdentifier SecP128r2 = new DerObjectIdentifier(EllipticCurve + ".29");
public static readonly DerObjectIdentifier SecP160r2 = new DerObjectIdentifier(EllipticCurve + ".30");
public static readonly DerObjectIdentifier SecP192k1 = new DerObjectIdentifier(EllipticCurve + ".31");
public static readonly DerObjectIdentifier SecP224k1 = new DerObjectIdentifier(EllipticCurve + ".32");
public static readonly DerObjectIdentifier SecP224r1 = new DerObjectIdentifier(EllipticCurve + ".33");
public static readonly DerObjectIdentifier SecP384r1 = new DerObjectIdentifier(EllipticCurve + ".34");
public static readonly DerObjectIdentifier SecP521r1 = new DerObjectIdentifier(EllipticCurve + ".35");
public static readonly DerObjectIdentifier SecT409k1 = new DerObjectIdentifier(EllipticCurve + ".36");
public static readonly DerObjectIdentifier SecT409r1 = new DerObjectIdentifier(EllipticCurve + ".37");
public static readonly DerObjectIdentifier SecT571k1 = new DerObjectIdentifier(EllipticCurve + ".38");
public static readonly DerObjectIdentifier SecT571r1 = new DerObjectIdentifier(EllipticCurve + ".39");
}
}
\ No newline at end of file
namespace NTumbleBit.BouncyCastle.Crypto
{
internal class Check
{
internal static void DataLength(bool condition, string msg)
{
if(condition)
throw new DataLengthException(msg);
}
internal static void DataLength(byte[] buf, int off, int len, string msg)
{
if(off + len > buf.Length)
throw new DataLengthException(msg);
}
internal static void OutputLength(byte[] buf, int off, int len, string msg)
{
if(off + len > buf.Length)
throw new OutputLengthException(msg);
}
}
}
namespace NTumbleBit.BouncyCastle.Crypto
{
/**
* all parameter classes implement this.
*/
internal interface ICipherParameters
{
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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