Commit a17f8ca6 authored by Jeremy Bokobza's avatar Jeremy Bokobza

Added Tracker object whose role is to figure out from where in the chain do we need to sync blocks.

parent a48af926
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
namespace Breeze.Wallet
{
public static class ChainExtensions
{
/// <summary>
/// Determines whether the chain is downloaded and up to date.
/// </summary>
/// <param name="chain">The chain.</param>
public static bool IsDownloaded(this ConcurrentChain chain)
{
return chain.Tip.Header.BlockTime.ToUnixTimeSeconds() > (DateTimeOffset.Now.ToUnixTimeSeconds() - TimeSpan.FromHours(1).TotalSeconds);
}
/// <summary>
/// Gets the type of the coin this chain relates to.
/// Obviously this method and how we figure out what coin we're on needs to be revisited.
/// </summary>
/// <param name="chain">The chain.</param>
/// <returns></returns>
/// <exception cref="System.Exception">No support for this coin.</exception>
public static CoinType GetCoinType(this ConcurrentChain chain)
{
uint256 genesis = chain.Genesis.Header.GetHash();
switch (genesis.ToString())
{
case "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f":
return CoinType.Bitcoin;
case "b0e511e965aeb40614ca65a1b79bd6e4e7ef299fa23e575a64b079691e9d4690":
return CoinType.Stratis;
case "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943":
return CoinType.Testnet;
default:
throw new Exception("No support for this coin.");
}
}
}
}
...@@ -2,12 +2,10 @@ ...@@ -2,12 +2,10 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.InteropServices;
using System.Security; using System.Security;
using Breeze.Wallet.Errors; using Breeze.Wallet.Errors;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Breeze.Wallet.Models; using Breeze.Wallet.Models;
using Breeze.Wallet.Wrappers;
using NBitcoin; using NBitcoin;
namespace Breeze.Wallet.Controllers namespace Breeze.Wallet.Controllers
...@@ -122,6 +120,9 @@ namespace Breeze.Wallet.Controllers ...@@ -122,6 +120,9 @@ namespace Breeze.Wallet.Controllers
DirectoryInfo walletFolder = GetWalletFolder(request.FolderPath); DirectoryInfo walletFolder = GetWalletFolder(request.FolderPath);
Wallet wallet = this.walletManager.RecoverWallet(request.Password, walletFolder.FullName, request.Name, request.Network, request.Mnemonic); Wallet wallet = this.walletManager.RecoverWallet(request.Password, walletFolder.FullName, request.Name, request.Network, request.Mnemonic);
// TODO give the tracker the date at which this wallet was originally created so that it can start syncing blocks for it
return this.Json(new WalletModel return this.Json(new WalletModel
{ {
Network = wallet.Network.Name, Network = wallet.Network.Name,
...@@ -372,23 +373,11 @@ namespace Breeze.Wallet.Controllers ...@@ -372,23 +373,11 @@ namespace Breeze.Wallet.Controllers
{ {
if (string.IsNullOrEmpty(folderPath)) if (string.IsNullOrEmpty(folderPath))
{ {
folderPath = GetDefaultWalletFolderPath(); folderPath = WalletManager.GetDefaultWalletFolderPath();
} }
return Directory.CreateDirectory(folderPath); return Directory.CreateDirectory(folderPath);
} }
/// <summary>
/// Gets the path of the default folder in which the wallets will be stored.
/// </summary>
/// <returns>The folder path for Windows, Linux or OSX systems.</returns>
private static string GetDefaultWalletFolderPath()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return $@"{Environment.GetEnvironmentVariable("AppData")}\Breeze";
}
return $"{Environment.GetEnvironmentVariable("HOME")}/.breeze";
}
} }
} }
using System.Threading.Tasks;
using NBitcoin;
namespace Breeze.Wallet
{
public interface ITracker
{
/// <summary>
/// Initializes the tracker.
/// </summary>
/// <returns></returns>
Task Initialize();
/// <summary>
/// Waits for the chain to download.
/// </summary>
/// <returns></returns>
Task WaitForChainDownloadAsync();
}
}
...@@ -66,7 +66,7 @@ namespace Breeze.Wallet ...@@ -66,7 +66,7 @@ namespace Breeze.Wallet
/// Creates the new address. /// Creates the new address.
/// </summary> /// </summary>
/// <param name="walletName">The name of the wallet in which this address will be created.</param> /// <param name="walletName">The name of the wallet in which this address will be created.</param>
/// <param name="coinType">the type of coin for which to create an account.</param> /// <param name="coinType">The type of coin for which to create an account.</param>
/// <param name="accountName">The name of the account in which this address will be created.</param> /// <param name="accountName">The name of the account in which this address will be created.</param>
/// <returns>The new address, in Base58 format.</returns> /// <returns>The new address, in Base58 format.</returns>
string CreateNewAddress(string walletName, CoinType coinType, string accountName); string CreateNewAddress(string walletName, CoinType coinType, string accountName);
...@@ -80,5 +80,20 @@ namespace Breeze.Wallet ...@@ -80,5 +80,20 @@ namespace Breeze.Wallet
WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType, bool allowUnconfirmed); WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType, bool allowUnconfirmed);
bool SendTransaction(string transactionHex); bool SendTransaction(string transactionHex);
/// <summary>
/// Processes a block received from the network.
/// </summary>
/// <param name="coinType">The type of coin this block relates to.</param>
/// <param name="height">The height of the block in the blockchain.</param>
/// <param name="block">The block.</param>
void ProcessBlock(CoinType coinType, int height, Block block);
/// <summary>
/// Processes a transaction received from the network.
/// </summary>
/// <param name="coinType">The type of coin this transaction relates to.</param>
/// <param name="transaction">The transaction.</param>
void ProcessTransaction(CoinType coinType, NBitcoin.Transaction transaction);
} }
} }
using NBitcoin; using NBitcoin;
using Stratis.Bitcoin; using Stratis.Bitcoin;
using Breeze.Wallet.Wrappers;
using Stratis.Bitcoin.Builder;
namespace Breeze.Wallet.Notifications namespace Breeze.Wallet.Notifications
{ {
...@@ -11,13 +9,14 @@ namespace Breeze.Wallet.Notifications ...@@ -11,13 +9,14 @@ namespace Breeze.Wallet.Notifications
public class BlockObserver : SignalObserver<Block> public class BlockObserver : SignalObserver<Block>
{ {
private readonly ConcurrentChain chain; private readonly ConcurrentChain chain;
private readonly CoinType coinType;
private readonly IWalletManager walletManager;
private readonly ITrackerWrapper trackerWrapper; public BlockObserver(ConcurrentChain chain, CoinType coinType, IWalletManager walletManager)
public BlockObserver(ConcurrentChain chain, ITrackerWrapper trackerWrapper)
{ {
this.chain = chain; this.chain = chain;
this.trackerWrapper = trackerWrapper; this.coinType = coinType;
this.walletManager = walletManager;
} }
/// <summary> /// <summary>
...@@ -29,7 +28,7 @@ namespace Breeze.Wallet.Notifications ...@@ -29,7 +28,7 @@ namespace Breeze.Wallet.Notifications
var hash = block.Header.GetHash(); var hash = block.Header.GetHash();
var height = this.chain.GetBlock(hash).Height; var height = this.chain.GetBlock(hash).Height;
this.trackerWrapper.NotifyAboutBlock(height, block); this.walletManager.ProcessBlock(this.coinType, height, block);
} }
} }
} }
using NBitcoin; using NBitcoin;
using Stratis.Bitcoin; using Stratis.Bitcoin;
using Breeze.Wallet.Wrappers;
namespace Breeze.Wallet.Notifications namespace Breeze.Wallet.Notifications
{ {
...@@ -9,11 +8,14 @@ namespace Breeze.Wallet.Notifications ...@@ -9,11 +8,14 @@ namespace Breeze.Wallet.Notifications
/// </summary> /// </summary>
public class TransactionObserver : SignalObserver<Transaction> public class TransactionObserver : SignalObserver<Transaction>
{ {
private readonly ITrackerWrapper trackerWrapper; private readonly CoinType coinType;
public TransactionObserver(ITrackerWrapper trackerWrapper) private readonly IWalletManager walletManager;
public TransactionObserver(CoinType coinType, IWalletManager walletManager)
{ {
this.trackerWrapper = trackerWrapper; this.coinType = coinType;
this.walletManager = walletManager;
} }
/// <summary> /// <summary>
...@@ -22,7 +24,7 @@ namespace Breeze.Wallet.Notifications ...@@ -22,7 +24,7 @@ namespace Breeze.Wallet.Notifications
/// <param name="transaction">The new transaction</param> /// <param name="transaction">The new transaction</param>
protected override void OnNextCore(Transaction transaction) protected override void OnNextCore(Transaction transaction)
{ {
this.trackerWrapper.NotifyAboutTransaction(transaction); this.walletManager.ProcessTransaction(this.coinType, transaction);
} }
} }
} }
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Breeze.Wallet.Notifications;
using NBitcoin;
using Stratis.Bitcoin;
using Stratis.Bitcoin.Notifications;
using Stratis.Bitcoin.Utilities;
namespace Breeze.Wallet
{
public class Tracker : ITracker
{
private readonly WalletManager walletManager;
private readonly ConcurrentChain chain;
private readonly Signals signals;
private readonly BlockNotification blockNotification;
private readonly CoinType coinType;
public Tracker(IWalletManager walletManager, ConcurrentChain chain, Signals signals, BlockNotification blockNotification)
{
this.walletManager = walletManager as WalletManager;
this.chain = chain;
this.signals = signals;
this.blockNotification = blockNotification;
this.coinType = chain.GetCoinType();
}
/// <inheritdoc />
public async Task Initialize()
{
// get the chain headers. This needs to be up-to-date before we really do anything
await this.WaitForChainDownloadAsync();
// subscribe to receiving blocks and transactions
BlockSubscriber sub = new BlockSubscriber(this.signals.Blocks, new BlockObserver(this.chain, this.coinType, this.walletManager));
sub.Subscribe();
TransactionSubscriber txSub = new TransactionSubscriber(this.signals.Transactions, new TransactionObserver(this.coinType, this.walletManager));
txSub.Subscribe();
// start syncing blocks
this.blockNotification.SyncFrom(this.chain.GetBlock(this.FindBestHeightForSyncing()).HashBlock);
}
private int FindBestHeightForSyncing()
{
// if there are no wallets, get blocks from now
if (!this.walletManager.Wallets.Any())
{
return this.chain.Tip.Height;
}
// sync the accounts with new blocks, starting from the most out of date
int? syncFromHeight = this.walletManager.Wallets.Min(w => w.AccountsRoot.Single(a => a.CoinType == this.coinType).LastBlockSyncedHeight);
if (syncFromHeight == null)
{
return this.chain.Tip.Height;
}
return Math.Min(syncFromHeight.Value, this.chain.Tip.Height);
}
/// <inheritdoc />
public Task WaitForChainDownloadAsync()
{
// make sure the chain is downloaded
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
return AsyncLoop.Run("WalletFeature.DownloadChain", token =>
{
// wait until the chain is downloaded. We wait until a block is from an hour ago.
if (this.chain.IsDownloaded())
{
cancellationTokenSource.Cancel();
}
return Task.CompletedTask;
},
cancellationTokenSource.Token,
repeatEvery: TimeSpans.FiveSeconds);
}
private bool BlocksSynced()
{
return this.walletManager.Wallets.All(w => w.AccountsRoot.Single(a => a.CoinType == this.coinType).LastBlockSyncedHeight == this.chain.Tip.Height);
}
}
}
...@@ -69,6 +69,12 @@ namespace Breeze.Wallet ...@@ -69,6 +69,12 @@ namespace Breeze.Wallet
[JsonProperty(PropertyName = "coinType")] [JsonProperty(PropertyName = "coinType")]
public CoinType CoinType { get; set; } public CoinType CoinType { get; set; }
/// <summary>
/// The height of the last block that was synced.
/// </summary>
[JsonProperty(PropertyName = "lastBlockSyncedHeight", NullValueHandling = NullValueHandling.Ignore)]
public int? LastBlockSyncedHeight { get; set; }
/// <summary> /// <summary>
/// The accounts used in the wallet. /// The accounts used in the wallet.
/// </summary> /// </summary>
...@@ -79,9 +85,22 @@ namespace Breeze.Wallet ...@@ -79,9 +85,22 @@ namespace Breeze.Wallet
/// <summary> /// <summary>
/// The type of coin, as specified in BIP44. /// The type of coin, as specified in BIP44.
/// </summary> /// </summary>
/// <remarks>For more, see https://github.com/satoshilabs/slips/blob/master/slip-0044.md</remarks>
public enum CoinType public enum CoinType
{ {
/// <summary>
/// Bitcoin
/// </summary>
Bitcoin = 0, Bitcoin = 0,
/// <summary>
/// Testnet (all coins)
/// </summary>
Testnet = 1,
/// <summary>
/// Stratis
/// </summary>
Stratis = 105 Stratis = 105
} }
......
using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Builder.Feature;
using Breeze.Wallet.Controllers; using Breeze.Wallet.Controllers;
using Breeze.Wallet.Notifications;
using Breeze.Wallet.Wrappers;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NBitcoin; using NBitcoin;
using Stratis.Bitcoin; using Stratis.Bitcoin;
...@@ -11,23 +9,24 @@ namespace Breeze.Wallet ...@@ -11,23 +9,24 @@ namespace Breeze.Wallet
{ {
public class WalletFeature : FullNodeFeature public class WalletFeature : FullNodeFeature
{ {
private readonly ITrackerWrapper trackerWrapper; private readonly ITracker tracker;
private readonly Signals signals; private readonly IWalletManager walletManager;
private readonly ConcurrentChain chain;
public WalletFeature(ITrackerWrapper trackerWrapper, Signals signals, ConcurrentChain chain) public WalletFeature(ITracker tracker, IWalletManager walletManager)
{ {
this.trackerWrapper = trackerWrapper; this.tracker = tracker;
this.signals = signals; this.walletManager = walletManager;
this.chain = chain;
} }
public override void Start() public override void Start()
{ {
BlockSubscriber sub = new BlockSubscriber(signals.Blocks, new BlockObserver(chain, trackerWrapper)); this.tracker.Initialize();
sub.Subscribe(); }
TransactionSubscriber txSub = new TransactionSubscriber(signals.Transactions, new TransactionObserver(trackerWrapper));
txSub.Subscribe(); public override void Stop()
{
this.walletManager.Dispose();
base.Stop();
} }
} }
...@@ -41,7 +40,7 @@ namespace Breeze.Wallet ...@@ -41,7 +40,7 @@ namespace Breeze.Wallet
.AddFeature<WalletFeature>() .AddFeature<WalletFeature>()
.FeatureServices(services => .FeatureServices(services =>
{ {
services.AddSingleton<ITrackerWrapper, TrackerWrapper>(); services.AddSingleton<ITracker, Tracker>();
services.AddSingleton<IWalletManager, WalletManager>(); services.AddSingleton<IWalletManager, WalletManager>();
services.AddSingleton<WalletController>(); services.AddSingleton<WalletController>();
}); });
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using Breeze.Wallet.Helpers; using Breeze.Wallet.Helpers;
using Breeze.Wallet.Models; using Breeze.Wallet.Models;
using NBitcoin; using NBitcoin;
using Newtonsoft.Json; using Newtonsoft.Json;
using Stratis.Bitcoin.Utilities; using Transaction = NBitcoin.Transaction;
namespace Breeze.Wallet namespace Breeze.Wallet
{ {
...@@ -20,6 +21,12 @@ namespace Breeze.Wallet ...@@ -20,6 +21,12 @@ namespace Breeze.Wallet
public WalletManager() public WalletManager()
{ {
this.Wallets = new List<Wallet>(); this.Wallets = new List<Wallet>();
// find wallets and load them in memory
foreach (var path in this.GetWalletFilesPaths())
{
this.Load(this.GetWallet(path));
}
} }
/// <inheritdoc /> /// <inheritdoc />
...@@ -37,6 +44,7 @@ namespace Breeze.Wallet ...@@ -37,6 +44,7 @@ namespace Breeze.Wallet
ExtKey extendedKey = mnemonic.DeriveExtKey(passphrase); ExtKey extendedKey = mnemonic.DeriveExtKey(passphrase);
// create a wallet file // create a wallet file
Wallet wallet = this.GenerateWalletFile(password, folderPath, name, WalletHelpers.GetNetwork(network), extendedKey); Wallet wallet = this.GenerateWalletFile(password, folderPath, name, WalletHelpers.GetNetwork(network), extendedKey);
this.Load(wallet); this.Load(wallet);
...@@ -48,11 +56,8 @@ namespace Breeze.Wallet ...@@ -48,11 +56,8 @@ namespace Breeze.Wallet
{ {
string walletFilePath = Path.Combine(folderPath, $"{name}.json"); string walletFilePath = Path.Combine(folderPath, $"{name}.json");
if (!File.Exists(walletFilePath))
throw new FileNotFoundException($"No wallet file found at {walletFilePath}");
// load the file from the local system // load the file from the local system
Wallet wallet = JsonConvert.DeserializeObject<Wallet>(File.ReadAllText(walletFilePath)); Wallet wallet = this.GetWallet(walletFilePath);
this.Load(wallet); this.Load(wallet);
return wallet; return wallet;
...@@ -199,8 +204,7 @@ namespace Breeze.Wallet ...@@ -199,8 +204,7 @@ namespace Breeze.Wallet
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
public WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType, public WalletBuildTransactionModel BuildTransaction(string password, string address, Money amount, string feeType, bool allowUnconfirmed)
bool allowUnconfirmed)
{ {
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
...@@ -210,6 +214,27 @@ namespace Breeze.Wallet ...@@ -210,6 +214,27 @@ namespace Breeze.Wallet
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
/// <inheritdoc />
public void ProcessBlock(CoinType coinType, int height, Block block)
{
Console.WriteLine($"block notification: height: {height}, block hash: {block.Header.GetHash()}, coin type: {coinType}");
// update the wallets with the last processed block height
foreach (var wallet in this.Wallets)
{
foreach (var accountRoot in wallet.AccountsRoot.Where(a => a.CoinType == coinType))
{
accountRoot.LastBlockSyncedHeight = height;
}
}
}
/// <inheritdoc />
public void ProcessTransaction(CoinType coinType, Transaction transaction)
{
Console.WriteLine($"transaction notification: tx hash {transaction.GetHash()}, coin type: {coinType}");
}
/// <inheritdoc /> /// <inheritdoc />
public void DeleteWallet(string walletFilePath) public void DeleteWallet(string walletFilePath)
{ {
...@@ -219,7 +244,11 @@ namespace Breeze.Wallet ...@@ -219,7 +244,11 @@ namespace Breeze.Wallet
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
// TODO Safely persist the wallet before disposing // safely persist the wallets to the file system before disposing
foreach (var wallet in this.Wallets)
{
this.SaveToFile(wallet);
}
} }
/// <summary> /// <summary>
...@@ -230,7 +259,6 @@ namespace Breeze.Wallet ...@@ -230,7 +259,6 @@ namespace Breeze.Wallet
/// <param name="name">The name of the wallet.</param> /// <param name="name">The name of the wallet.</param>
/// <param name="network">The network this wallet is for.</param> /// <param name="network">The network this wallet is for.</param>
/// <param name="extendedKey">The root key used to generate keys.</param> /// <param name="extendedKey">The root key used to generate keys.</param>
/// <param name="coinType">The type of coin for which this wallet is created.</param>
/// <param name="creationTime">The time this wallet was created.</param> /// <param name="creationTime">The time this wallet was created.</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="System.NotSupportedException"></exception> /// <exception cref="System.NotSupportedException"></exception>
...@@ -250,8 +278,10 @@ namespace Breeze.Wallet ...@@ -250,8 +278,10 @@ namespace Breeze.Wallet
Network = network, Network = network,
AccountsRoot = new List<AccountRoot> { AccountsRoot = new List<AccountRoot> {
new AccountRoot { Accounts = new List<HdAccount>(), CoinType = CoinType.Bitcoin }, new AccountRoot { Accounts = new List<HdAccount>(), CoinType = CoinType.Bitcoin },
new AccountRoot { Accounts = new List<HdAccount>(), CoinType = CoinType.Testnet },
new AccountRoot { Accounts = new List<HdAccount>(), CoinType = CoinType.Stratis} }, new AccountRoot { Accounts = new List<HdAccount>(), CoinType = CoinType.Stratis} },
WalletFilePath = walletFilePath WalletFilePath = walletFilePath,
}; };
// create a folder if none exists and persist the file // create a folder if none exists and persist the file
...@@ -270,16 +300,33 @@ namespace Breeze.Wallet ...@@ -270,16 +300,33 @@ namespace Breeze.Wallet
File.WriteAllText(wallet.WalletFilePath, JsonConvert.SerializeObject(wallet, Formatting.Indented)); File.WriteAllText(wallet.WalletFilePath, JsonConvert.SerializeObject(wallet, Formatting.Indented));
} }
/// <summary>
/// Gets the wallet located at the specified path.
/// </summary>
/// <param name="walletFilePath">The wallet file path.</param>
/// <returns></returns>
/// <exception cref="System.IO.FileNotFoundException"></exception>
private Wallet GetWallet(string walletFilePath)
{
if (!File.Exists(walletFilePath))
throw new FileNotFoundException($"No wallet file found at {walletFilePath}");
// load the file from the local system
return JsonConvert.DeserializeObject<Wallet>(File.ReadAllText(walletFilePath));
}
/// <summary> /// <summary>
/// Loads the wallet to be used by the manager. /// Loads the wallet to be used by the manager.
/// </summary> /// </summary>
/// <param name="wallet">The wallet to load.</param> /// <param name="wallet">The wallet to load.</param>
private void Load(Wallet wallet) private void Load(Wallet wallet)
{ {
if (this.Wallets.All(w => w.Name != wallet.Name)) if (this.Wallets.Any(w => w.Name == wallet.Name))
{ {
this.Wallets.Add(wallet); return;
} }
this.Wallets.Add(wallet);
} }
private BitcoinPubKeyAddress GenerateAddress(string accountExtPubKey, int index, bool isChange, Network network) private BitcoinPubKeyAddress GenerateAddress(string accountExtPubKey, int index, bool isChange, Network network)
...@@ -290,6 +337,14 @@ namespace Breeze.Wallet ...@@ -290,6 +337,14 @@ namespace Breeze.Wallet
return extPubKey.PubKey.GetAddress(network); return extPubKey.PubKey.GetAddress(network);
} }
private IEnumerable<string> GetWalletFilesPaths()
{
// TODO look in user-chosen folder as well.
// maybe the api can maintain a list of wallet paths it knows about
var defaultFolderPath = GetDefaultWalletFolderPath();
return Directory.EnumerateFiles(defaultFolderPath, "*.json", SearchOption.TopDirectoryOnly);
}
/// <summary> /// <summary>
/// Creates the bip44 path. /// Creates the bip44 path.
/// </summary> /// </summary>
...@@ -306,5 +361,19 @@ namespace Breeze.Wallet ...@@ -306,5 +361,19 @@ namespace Breeze.Wallet
int change = isChange ? 1 : 0; int change = isChange ? 1 : 0;
return $"m/44'/{(int)coinType}'/{accountIndex}'/{change}/{addressIndex}"; return $"m/44'/{(int)coinType}'/{accountIndex}'/{change}/{addressIndex}";
} }
/// <summary>
/// Gets the path of the default folder in which the wallets will be stored.
/// </summary>
/// <returns>The folder path for Windows, Linux or OSX systems.</returns>
public static string GetDefaultWalletFolderPath()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return $@"{Environment.GetEnvironmentVariable("AppData")}\Breeze";
}
return $"{Environment.GetEnvironmentVariable("HOME")}/.breeze";
}
} }
} }
using NBitcoin;
namespace Breeze.Wallet.Wrappers
{
public interface ITrackerWrapper
{
void NotifyAboutBlock(int height, Block block);
void NotifyAboutTransaction(Transaction transaction);
uint256 GetLastProcessedBlock();
}
}
using NBitcoin;
using System;
namespace Breeze.Wallet.Wrappers
{
public class TrackerWrapper : ITrackerWrapper
{
// private readonly Tracker tracker;
public TrackerWrapper(Network network)
{
//this.tracker = new Tracker(network);
}
/// <summary>
/// Get the hash of the last block that has been succesfully processed.
/// </summary>
/// <returns>The hash of the block</returns>
public uint256 GetLastProcessedBlock()
{
// TODO use Tracker.BestHeight. Genesis hash for now.
return uint256.Parse("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
}
public void NotifyAboutBlock(int height, Block block)
{
// this.tracker.AddOrReplaceBlock(new Height(height), block);
Console.WriteLine($"block notification: height: {height}, block hash: {block.Header.GetHash()}");
}
public void NotifyAboutTransaction(Transaction transaction)
{
// TODO what should the height be? is it necessary?
// this.tracker.ProcessTransaction(new SmartTransaction(transaction, new Height(0)));
Console.WriteLine($"transaction notification: tx hash {transaction.GetHash()}");
}
}
}
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