Commit 10ffe1bf authored by Jeremy Bokobza's avatar Jeremy Bokobza Committed by GitHub

Merge pull request #91 from bokobza/master

Added support for the tumbling bit to receive blocks
parents c0c57956 618c100a
using Breeze.TumbleBit.Client;
using NBitcoin;
using Stratis.Bitcoin;
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);
}
}
}
...@@ -4,6 +4,7 @@ using System.Net; ...@@ -4,6 +4,7 @@ using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Breeze.TumbleBit.Client; using Breeze.TumbleBit.Client;
using Breeze.TumbleBit.Models;
using Stratis.Bitcoin.Common.JsonErrors; using Stratis.Bitcoin.Common.JsonErrors;
namespace Breeze.TumbleBit.Controllers namespace Breeze.TumbleBit.Controllers
...@@ -25,8 +26,8 @@ namespace Breeze.TumbleBit.Controllers ...@@ -25,8 +26,8 @@ namespace Breeze.TumbleBit.Controllers
/// Connect to a tumbler. /// Connect to a tumbler.
/// </summary> /// </summary>
[Route("connect")] [Route("connect")]
[HttpGet] [HttpPost]
public async Task<IActionResult> ConnectAsync() public async Task<IActionResult> ConnectAsync([FromBody] TumblerConnectionRequest request)
{ {
// checks the request is valid // checks the request is valid
if (!this.ModelState.IsValid) if (!this.ModelState.IsValid)
...@@ -37,7 +38,7 @@ namespace Breeze.TumbleBit.Controllers ...@@ -37,7 +38,7 @@ namespace Breeze.TumbleBit.Controllers
try try
{ {
var tumblerParameters = await this.tumbleBitManager.ConnectToTumblerAsync(); var tumblerParameters = await this.tumbleBitManager.ConnectToTumblerAsync(request.ServerAddress);
return this.Json(tumblerParameters); return this.Json(tumblerParameters);
} }
catch (Exception e) catch (Exception e)
...@@ -45,5 +46,30 @@ namespace Breeze.TumbleBit.Controllers ...@@ -45,5 +46,30 @@ namespace Breeze.TumbleBit.Controllers
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"An error occured connecting to the tumbler.", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"An error occured connecting to the tumbler.", 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.DestinationWalletName);
return this.Ok();
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"An error occured connecting to the tumbler.", e.ToString());
}
}
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NBitcoin;
using NTumbleBit.ClassicTumbler; using NTumbleBit.ClassicTumbler;
namespace Breeze.TumbleBit.Client namespace Breeze.TumbleBit.Client
...@@ -14,7 +15,17 @@ namespace Breeze.TumbleBit.Client ...@@ -14,7 +15,17 @@ namespace Breeze.TumbleBit.Client
/// <summary> /// <summary>
/// Connects to the tumbler. /// Connects to the tumbler.
/// </summary> /// </summary>
/// <param name="serverAddress">The URI of the tumbler.</param>
/// <returns></returns> /// <returns></returns>
Task<ClassicTumblerParameters> ConnectToTumblerAsync(); Task<ClassicTumblerParameters> ConnectToTumblerAsync(Uri serverAddress);
Task TumbleAsync(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);
} }
} }
...@@ -25,4 +25,10 @@ namespace Breeze.TumbleBit.Models ...@@ -25,4 +25,10 @@ namespace Breeze.TumbleBit.Models
public string Network { get; set; } public string Network { get; set; }
} }
public class TumbleRequest
{
[Required(ErrorMessage = "A wallet name is required.")]
public string DestinationWalletName { get; set; }
}
} }
...@@ -35,7 +35,7 @@ namespace Breeze.TumbleBit ...@@ -35,7 +35,7 @@ namespace Breeze.TumbleBit
.AddFeature<TumbleBitFeature>() .AddFeature<TumbleBitFeature>()
.FeatureServices(services => .FeatureServices(services =>
{ {
services.AddSingleton<ITumbleBitManager>(new TumbleBitManager(serverAddress)); services.AddSingleton<ITumbleBitManager, TumbleBitManager> ();
services.AddSingleton<TumbleBitController>(); services.AddSingleton<TumbleBitController>();
}); });
}); });
......
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NTumbleBit.ClassicTumbler;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Logging;
using Stratis.Bitcoin.Wallet;
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 IWalletManager walletManager;
private readonly ILogger logger;
private readonly Signals signals;
private readonly ConcurrentChain chain;
private ClassicTumblerParameters TumblerParameters { get; set; }
public TumbleBitManager(ILoggerFactory loggerFactory, IWalletManager walletManager, ConcurrentChain chain, Network network, Signals signals)
{
this.walletManager = walletManager;
this.chain = chain;
this.signals = signals;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
}
/// <inheritdoc />
public async Task<ClassicTumblerParameters> ConnectToTumblerAsync(Uri serverAddress)
{
this.tumblerService = new TumblerService(serverAddress);
this.TumblerParameters = await this.tumblerService.GetClassicTumblerParametersAsync();
return this.TumblerParameters;
}
/// <inheritdoc />
public Task TumbleAsync(string destinationWalletName)
{
if (this.TumblerParameters == null || this.tumblerService == null)
{
throw new Exception("Please connect to the tumbler first.");
}
Wallet destinationWallet = this.walletManager.GetWallet(destinationWalletName);
if (destinationWallet == null)
{
throw new Exception($"Destination not found. Have you created a wallet with name {destinationWalletName}?");
}
// subscribe to receiving blocks and transactions
this.signals.Blocks.Subscribe(new BlockObserver(this.chain, this));
return Task.CompletedTask;
}
/// <inheritdoc />
public void ProcessBlock(int height, Block block)
{
// TODO start the state machine
this.logger.LogDebug($"Receive block with height {height}");
}
}
}
using System;
using System.Threading.Tasks;
using NTumbleBit.ClassicTumbler;
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;
public TumbleBitManager(Uri serverAddress)
{
this.InitializeTumblerService(serverAddress);
}
/// <summary>
/// Initializes the tumbler service.
/// </summary>
/// <param name="serverAddress">The server address.</param>
public void InitializeTumblerService(Uri serverAddress)
{
this.tumblerService = new TumblerService(serverAddress);
}
/// <inheritdoc />
public async Task<ClassicTumblerParameters> ConnectToTumblerAsync()
{
return await this.tumblerService.GetClassicTumblerParametersAsync();
}
}
}
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