Commit 1f733aa0 authored by Jeremy Bokobza's avatar Jeremy Bokobza

Added wallet endpoints for balance, history, info, build transaction and send transaction

parent 76cff8d4
......@@ -21,7 +21,7 @@ namespace Breeze.Api.Tests
var controller = new WalletController(mockWalletCreate.Object);
// Act
var result = controller.Create(new WalletCreationModel
var result = controller.Create(new WalletCreationRequest
{
Name = "myName",
FolderPath = "",
......@@ -52,7 +52,7 @@ namespace Breeze.Api.Tests
var controller = new WalletController(mockWalletWrapper.Object);
// Act
var result = controller.Recover(new WalletRecoveryModel
var result = controller.Recover(new WalletRecoveryRequest
{
Name = "myName",
FolderPath = "",
......@@ -87,7 +87,7 @@ namespace Breeze.Api.Tests
var controller = new WalletController(mockWalletWrapper.Object);
// Act
var result = controller.Load(new WalletLoadModel
var result = controller.Load(new WalletLoadRequest
{
Name = "myName",
FolderPath = "",
......@@ -113,7 +113,7 @@ namespace Breeze.Api.Tests
var controller = new WalletController(mockWalletWrapper.Object);
// Act
var result = controller.Load(new WalletLoadModel
var result = controller.Load(new WalletLoadRequest
{
Name = "myName",
FolderPath = "",
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using Breeze.Wallet.Errors;
using Microsoft.AspNetCore.Mvc;
using Breeze.Wallet.Models;
using Breeze.Wallet.Wrappers;
......@@ -25,13 +27,13 @@ namespace Breeze.Wallet.Controllers
/// <param name="walletCreation">The object containing the parameters used to create the wallet.</param>
/// <returns>A JSON object containing the mnemonic created for the new wallet.</returns>
[HttpPost]
public IActionResult Create([FromBody]WalletCreationModel walletCreation)
public IActionResult Create([FromBody]WalletCreationRequest walletCreation)
{
// checks the request is valid
if (!this.ModelState.IsValid)
{
var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
return this.BadRequest(string.Join(Environment.NewLine, errors));
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors));
}
try
......@@ -41,21 +43,19 @@ namespace Breeze.Wallet.Controllers
}
catch (NotSupportedException e)
{
Console.WriteLine(e);
// indicates that this wallet already exists
return this.StatusCode((int) HttpStatusCode.Conflict, "This wallet already exists.");
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Conflict, "This wallet already exists.", e.ToString());
}
}
[HttpGet]
public IActionResult Load([FromQuery]WalletLoadModel walletLoad)
public IActionResult Load([FromQuery]WalletLoadRequest walletLoad)
{
// checks the request is valid
if (!this.ModelState.IsValid)
{
var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
return this.BadRequest(string.Join(Environment.NewLine, errors));
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors));
}
try
......@@ -66,34 +66,28 @@ namespace Breeze.Wallet.Controllers
}
catch (FileNotFoundException e)
{
Console.WriteLine(e);
// indicates that this wallet does not exist
return this.StatusCode((int)HttpStatusCode.NotFound, "Wallet not found.");
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Conflict, "This wallet already exists.", e.ToString());
}
catch (SecurityException e)
{
Console.WriteLine(e);
// indicates that the password is wrong
return this.StatusCode((int)HttpStatusCode.Forbidden, "Wrong password, please try again.");
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Forbidden, "Wrong password, please try again.", e.ToString());
}
catch (Exception e)
{
Console.WriteLine(e);
return this.StatusCode((int)HttpStatusCode.BadRequest, e.Message);
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("recover")]
[HttpPost]
public IActionResult Recover([FromBody]WalletRecoveryModel walletRecovery)
public IActionResult Recover([FromBody]WalletRecoveryRequest walletRecovery)
{
// checks the request is valid
if (!this.ModelState.IsValid)
{
var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
return this.BadRequest(string.Join(Environment.NewLine, errors));
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors));
}
try
......@@ -104,22 +98,132 @@ namespace Breeze.Wallet.Controllers
}
catch (FileNotFoundException e)
{
Console.WriteLine(e);
// indicates that this wallet does not exist
return this.StatusCode((int)HttpStatusCode.NotFound, "Wallet not found.");
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.NotFound, "Wallet not found.", e.ToString());
}
catch (SecurityException e)
{
Console.WriteLine(e);
// indicates that the password is wrong
return this.StatusCode((int)HttpStatusCode.Forbidden, "Wrong password, please try again.");
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Forbidden, "Wrong password, please try again.", e.ToString());
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("info")]
[HttpGet]
public IActionResult GetInfo([FromQuery] WalletName model)
{
// 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
{
return this.Json(this.walletWrapper.GetInfo(model.Name));
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("history")]
[HttpGet]
public IActionResult GetHistory([FromQuery] WalletName model)
{
// 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
{
return this.Json(this.walletWrapper.GetHistory(model.Name));
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("balance")]
[HttpGet]
public IActionResult GetBalance([FromQuery] WalletName model)
{
// 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
{
return this.Json(this.walletWrapper.GetBalance(model.Name));
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("build-transaction")]
[HttpPost]
public IActionResult BuildTransaction([FromQuery] BuildTransactionRequest 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
{
return this.Json(this.walletWrapper.BuildTransaction(request.Password, request.Address, request.Amount, request.FeeType, request.AllowUnconfirmed));
}
catch (Exception e)
{
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
[Route("send-transaction")]
[HttpPost]
public IActionResult SendTransaction([FromQuery] SendTransactionRequest 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 result = this.walletWrapper.SendTransaction(request.Hex);
if (result)
{
return this.Ok();
}
return this.StatusCode((int)HttpStatusCode.BadRequest);
}
catch (Exception e)
{
Console.WriteLine(e);
return this.StatusCode((int)HttpStatusCode.BadRequest, e.Message);
return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString());
}
}
}
......
using System.Collections.Generic;
using System.Net;
namespace Breeze.Wallet.Errors
{
public static class ErrorHelpers
{
public static ErrorResult BuildErrorResponse(HttpStatusCode statusCode, string message, string description)
{
ErrorResponse errorResponse = new ErrorResponse
{
Errors = new List<ErrorModel>
{
new ErrorModel
{
Status = (int) statusCode,
Message = message,
Description = description
}
}
};
return new ErrorResult((int)statusCode, errorResponse);
}
}
}
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Breeze.Wallet.Errors
{
public class ErrorResponse
{
[JsonProperty(PropertyName = "errors")]
public List<ErrorModel> Errors { get; set; }
}
public class ErrorModel
{
[JsonProperty(PropertyName = "status")]
public int Status { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Breeze.Wallet.Errors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Breeze.Wallet.Errors
{
public class ErrorResult : ObjectResult
{
public ErrorResult(int statusCode, ErrorResponse value) : base(value)
{
StatusCode = statusCode;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletBalanceModel
{
[JsonProperty(PropertyName = "isSynced")]
public bool IsSynced { get; set; }
[JsonProperty(PropertyName = "confirmed")]
public Money Confirmed { get; set; }
[JsonProperty(PropertyName = "unconfirmed")]
public Money Unconfirmed { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletBuildTransactionModel
{
[JsonProperty(PropertyName = "spendsUnconfirmed")]
public bool SpendsUnconfirmed { get; set; }
[JsonProperty(PropertyName = "fee")]
public Money Fee { get; set; }
[JsonProperty(PropertyName = "feePercentOfSent")]
public double FeePercentOfSent { get; set; }
[JsonProperty(PropertyName = "hex")]
public string Hex { get; set; }
[JsonProperty(PropertyName = "transaction")]
public Transaction Transaction { get; set; }
}
public class Transaction
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "isCoinbase")]
public bool IsCoinbase { get; set; }
[JsonProperty(PropertyName = "block")]
public string Block { get; set; }
[JsonProperty(PropertyName = "spentCoins")]
public IEnumerable<TransactionDetails> SpentCoins { get; set; }
[JsonProperty(PropertyName = "receivedCoins")]
public IEnumerable<TransactionDetails> ReceivedCoins { get; set; }
[JsonProperty(PropertyName = "firstSendDate")]
public DateTime FirstSeenDate { get; set; }
[JsonProperty(PropertyName = "fees")]
public Money Fees { get; set; }
}
public class TransactionDetails
{
[JsonProperty(PropertyName = "transactionId")]
public string TransactionId { get; set; }
[JsonProperty(PropertyName = "index")]
public int Index { get; set; }
[JsonProperty(PropertyName = "value")]
public int Value { get; set; }
[JsonProperty(PropertyName = "scriptPubKey")]
public string ScriptPubKey { get; set; }
[JsonProperty(PropertyName = "redeemScript")]
public string RedeemScript { get; set; }
}
}
......@@ -7,7 +7,7 @@ namespace Breeze.Wallet.Models
/// <summary>
/// Object used to create a new wallet
/// </summary>
public class WalletCreationModel
public class WalletCreationRequest
{
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
......@@ -21,7 +21,7 @@ namespace Breeze.Wallet.Models
public string Name { get; set; }
}
public class WalletLoadModel
public class WalletLoadRequest
{
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
......@@ -33,7 +33,7 @@ namespace Breeze.Wallet.Models
public string Name { get; set; }
}
public class WalletRecoveryModel
public class WalletRecoveryRequest
{
[Required(ErrorMessage = "A mnemonic is required.")]
public string Mnemonic { get; set; }
......@@ -49,4 +49,34 @@ namespace Breeze.Wallet.Models
public string Network { get; set; }
}
public class WalletName
{
[Required(ErrorMessage = "The name of the wallet is missing.")]
public string Name { get; set; }
}
public class BuildTransactionRequest
{
[Required(ErrorMessage = "A password is required.")]
public string Password { get; set; }
[Required(ErrorMessage = "A destination address is required.")]
public string Address { get; set; }
[Required(ErrorMessage = "An amount is required.")]
public string Amount { get; set; }
[Required(ErrorMessage = "A fee type is required. It can be 'low', 'medium' or 'high'.")]
public string FeeType { get; set; }
public bool AllowUnconfirmed { get; set; }
}
public class SendTransactionRequest
{
[Required(ErrorMessage = "A transaction in hexadecimal format is required.")]
public string Hex { get; set; }
}
}
using System.Collections.Generic;
using NBitcoin;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletHistoryModel
{
[JsonProperty(PropertyName = "transactions")]
public List<TransactionItem> Transactions { get; set; }
}
public class TransactionItem
{
[JsonProperty(PropertyName = "txId")]
public string TransactionId { get; set; }
[JsonProperty(PropertyName = "amount")]
public Money Amount { get; set; }
[JsonProperty(PropertyName = "confirmed")]
public Money Confirmed { get; set; }
[JsonProperty(PropertyName = "timestamp")]
public string Timestamp { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Breeze.Wallet.Models
{
public class WalletInfoModel
{
[JsonProperty(PropertyName = "filePath")]
public string FilePath { get; set; }
[JsonProperty(PropertyName = "encryptedSeed")]
public string EncryptedSeed { get; set; }
[JsonProperty(PropertyName = "chainCode")]
public string ChainCode { get; set; }
[JsonProperty(PropertyName = "network")]
public string Network { get; set; }
[JsonProperty(PropertyName = "creationTime")]
public string CreationTime { get; set; }
[JsonProperty(PropertyName = "isDecrypted")]
public bool IsDecrypted { get; set; }
[JsonProperty(PropertyName = "uniqueId")]
public string UniqueId { get; set; }
}
}
......@@ -2,15 +2,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Breeze.Wallet
namespace Breeze.Wallet.Models
{
public class WalletModel
{
[JsonProperty(PropertyName = "network")]
public string Network { get; set; }
[JsonProperty(PropertyName = "fileName")]
public string FileName { get; set; }
[JsonProperty(PropertyName = "addresses")]
public IEnumerable<string> Addresses { get; set; }
}
}

using Breeze.Wallet.Models;
using HBitcoin.Models;
using NBitcoin;
namespace Breeze.Wallet.Wrappers
{
/// <summary>
......@@ -6,10 +10,21 @@ namespace Breeze.Wallet.Wrappers
/// </summary>
public interface IWalletWrapper
{
string Create(string password, string folderPath, string name, string network);
WalletModel Load(string password, string folderPath, string name);
WalletModel Recover(string password, string folderPath, string name, string network, string mnemonic);
WalletInfoModel GetInfo(string walletName);
WalletBalanceModel GetBalance(string walletName);
WalletHistoryModel GetHistory(string walletName);
WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType, bool allowUnconfirmed);
bool SendTransaction(string transactionHex);
}
}
using System.IO;
using System.Linq;
using Breeze.Wallet.Models;
using HBitcoin.KeyManagement;
using NBitcoin;
......@@ -79,5 +80,31 @@ namespace Breeze.Wallet.Wrappers
return Network.TestNet;
}
}
public WalletInfoModel GetInfo(string name)
{
throw new System.NotImplementedException();
}
public WalletBalanceModel GetBalance(string walletName)
{
throw new System.NotImplementedException();
}
public WalletHistoryModel GetHistory(string walletName)
{
throw new System.NotImplementedException();
}
public WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType,
bool allowUnconfirmed)
{
throw new System.NotImplementedException();
}
public bool SendTransaction(string transactionHex)
{
throw new System.NotImplementedException();
}
}
}
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