Commit ae7993d1 authored by Jeremy Bokobza's avatar Jeremy Bokobza

SyncManager now doesn't wait for the full chain to be downloaded but rather...

SyncManager now doesn't wait for the full chain to be downloaded but rather wait until we have enough data to start asking for blocks
parent e7b987a5
...@@ -4,6 +4,7 @@ using System.Threading; ...@@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NBitcoin; using NBitcoin;
using NBitcoin.JsonConverters;
using Stratis.Bitcoin; using Stratis.Bitcoin;
using Stratis.Bitcoin.Notifications; using Stratis.Bitcoin.Notifications;
using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities;
...@@ -20,11 +21,11 @@ namespace Breeze.Wallet ...@@ -20,11 +21,11 @@ namespace Breeze.Wallet
private readonly CoinType coinType; private readonly CoinType coinType;
private readonly ILogger logger; private readonly ILogger logger;
private readonly Signals signals; private readonly Signals signals;
private readonly FullNode.CancellationProvider cancellationProvider;
private ChainedBlock walletTip; private ChainedBlock walletTip;
public LightWalletSyncManager(ILoggerFactory loggerFactory, IWalletManager walletManager, ConcurrentChain chain, Network network, public LightWalletSyncManager(ILoggerFactory loggerFactory, IWalletManager walletManager, ConcurrentChain chain, Network network,
BlockNotification blockNotification, Signals signals) BlockNotification blockNotification, Signals signals, FullNode.CancellationProvider cancellationProvider)
{ {
this.walletManager = walletManager as WalletManager; this.walletManager = walletManager as WalletManager;
this.chain = chain; this.chain = chain;
...@@ -32,11 +33,18 @@ namespace Breeze.Wallet ...@@ -32,11 +33,18 @@ namespace Breeze.Wallet
this.blockNotification = blockNotification; this.blockNotification = blockNotification;
this.coinType = (CoinType)network.Consensus.CoinType; this.coinType = (CoinType)network.Consensus.CoinType;
this.logger = loggerFactory.CreateLogger(this.GetType().FullName); this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
this.cancellationProvider = cancellationProvider;
} }
/// <inheritdoc /> /// <inheritdoc />
public Task Initialize() public Task Initialize()
{ {
// subscribe to receiving blocks and transactions
BlockSubscriber sub = new BlockSubscriber(this.signals.Blocks, new BlockObserver(this));
sub.Subscribe();
TransactionSubscriber txSub = new TransactionSubscriber(this.signals.Transactions, new TransactionObserver(this));
txSub.Subscribe();
// if there is no wallet created yet, the wallet tip is the chain tip. // if there is no wallet created yet, the wallet tip is the chain tip.
if (!this.walletManager.Wallets.Any()) if (!this.walletManager.Wallets.Any())
{ {
...@@ -45,7 +53,7 @@ namespace Breeze.Wallet ...@@ -45,7 +53,7 @@ namespace Breeze.Wallet
else else
{ {
this.walletTip = this.chain.GetBlock(this.walletManager.WalletTipHash); this.walletTip = this.chain.GetBlock(this.walletManager.WalletTipHash);
if (this.walletTip == null) if (this.walletTip == null && this.chain.Height > 0)
{ {
// the wallet tip was not found in the main chain. // the wallet tip was not found in the main chain.
// this can happen if the node crashes unexpectedly. // this can happen if the node crashes unexpectedly.
...@@ -62,58 +70,22 @@ namespace Breeze.Wallet ...@@ -62,58 +70,22 @@ namespace Breeze.Wallet
this.walletManager.WalletTipHash = fork.HashBlock; this.walletManager.WalletTipHash = fork.HashBlock;
this.walletTip = fork; this.walletTip = fork;
} }
}
// subscribe to receiving blocks and transactions
BlockSubscriber sub = new BlockSubscriber(this.signals.Blocks, new BlockObserver(this));
sub.Subscribe();
TransactionSubscriber txSub = new TransactionSubscriber(this.signals.Transactions, new TransactionObserver(this));
txSub.Subscribe();
// start syncing blocks
var bestHeightForSyncing = this.FindBestHeightForSyncing();
this.blockNotification.SyncFrom(this.chain.GetBlock(bestHeightForSyncing).HashBlock);
this.logger.LogInformation($"Tracker initialized. Syncing from {bestHeightForSyncing}.");
return Task.CompletedTask;
}
private int FindBestHeightForSyncing() // we're looking from where to start syncing the wallets.
{ // we start by looking at the heights of the wallets and we start syncing from the oldest one (the smallest height).
// if there are no wallets, get blocks from now // if for some reason we can't find a height, we look at the creation date of the wallets and we start syncing from the earliest date.
if (!this.walletManager.Wallets.Any()) int? smallestBlockHeightInWallet = this.walletManager.Wallets.Min(w => w.AccountsRoot.Single(a => a.CoinType == this.coinType).LastBlockSyncedHeight);
if (smallestBlockHeightInWallet == null)
{ {
return this.chain.Tip.Height; DateTimeOffset oldestWalletDate = this.walletManager.Wallets.Min(w => w.CreationTime);
this.SyncFrom(oldestWalletDate.LocalDateTime);
} }
else
// 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; this.SyncFrom(smallestBlockHeightInWallet.Value);
} }
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())
{
this.logger.LogInformation($"Chain downloaded. Tip height is {this.chain.Tip.Height}.");
cancellationTokenSource.Cancel();
}
return Task.CompletedTask; return Task.CompletedTask;
},
cancellationTokenSource.Token,
repeatEvery: TimeSpans.FiveSeconds);
} }
/// <inheritdoc /> /// <inheritdoc />
...@@ -157,18 +129,65 @@ namespace Breeze.Wallet ...@@ -157,18 +129,65 @@ namespace Breeze.Wallet
this.walletManager.ProcessBlock(block, this.walletTip); this.walletManager.ProcessBlock(block, this.walletTip);
} }
/// <inheritdoc />
public void ProcessTransaction(Transaction transaction) public void ProcessTransaction(Transaction transaction)
{ {
this.walletManager.ProcessTransaction(transaction); this.walletManager.ProcessTransaction(transaction);
} }
/// <inheritdoc />
public void SyncFrom(DateTime date) public void SyncFrom(DateTime date)
{ {
int blockSyncStart = this.chain.GetHeightAtTime(date); // before we start syncing we need to make sure that the chain is at a certain level.
this.SyncFrom(blockSyncStart); // if the chain is behind the date from which we want to sync, we wait for it to catch up, and then we start syncing.
// if the chain is already past the date we want to sync from, we don't wait, even though the chain might not be fully downloaded.
if (this.chain.Tip.Header.BlockTime.LocalDateTime < date)
{
AsyncLoop.RunUntil("WalletFeature.DownloadChain", this.cancellationProvider.Cancellation.Token,
() => this.chain.Tip.Header.BlockTime.LocalDateTime >= date,
() => this.StartSync(this.chain.GetHeightAtTime(date)),
(ex) =>
{
// in case of an exception while waiting for the chain to be at a certain height, we just cut our losses and
// sync from the current height.
this.logger.LogError($"Exception occurred while waiting for chain to download: {ex.Message}");
this.StartSync(this.chain.Tip.Height);
},
TimeSpans.FiveSeconds);
}
else
{
this.StartSync(this.chain.GetHeightAtTime(date));
}
} }
/// <inheritdoc />
public void SyncFrom(int height) public void SyncFrom(int height)
{
// before we start syncing we need to make sure that the chain is at a certain level.
// if the chain is behind the height from which we want to sync, we wait for it to catch up, and then we start syncing.
// if the chain is already past the height we want to sync from, we don't wait, even though the chain might not be fully downloaded.
if (this.chain.Tip.Height < height)
{
AsyncLoop.RunUntil("WalletFeature.DownloadChain", this.cancellationProvider.Cancellation.Token,
() => this.chain.Tip.Height >= height,
() => this.StartSync(height),
(ex) =>
{
// in case of an exception while waiting for the chain to be at a certain height, we just cut our losses and
// sync from the current height.
this.logger.LogError($"Exception occurred while waiting for chain to download: {ex.Message}");
this.StartSync(this.chain.Tip.Height);
},
TimeSpans.FiveSeconds);
}
else
{
this.StartSync(height);
}
}
private void StartSync(int height)
{ {
var chainedBlock = this.chain.GetBlock(height); var chainedBlock = this.chain.GetBlock(height);
if (chainedBlock == null) if (chainedBlock == null)
......
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