Skip to content
This repository has been archived by the owner on Aug 16, 2021. It is now read-only.

[3.0.8.0] Fast Rewind #4082

Open
wants to merge 23 commits into
base: release/3.0.8.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public NonBackedCoinView()
throw new NotImplementedException();
}

public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down Expand Up @@ -166,7 +166,7 @@ public BackedCoinView1(BackedCoinView2 inner, int outputCount = 0)
throw new NotImplementedException();
}

public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down Expand Up @@ -206,7 +206,7 @@ public BackedCoinView2(NonBackedCoinView inner, int outputCount = 0)
throw new NotImplementedException();
}

public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,13 @@ public async Task TestRewindAsync()

await this.ValidateCoinviewIntegrityAsync(outPoints);

for (int i = 0; i < addChangesTimes; i++)
var random = new Random();

while (currentHeight > 1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to loop until random returns a 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we keep rewinding between >0% and 100% of the remaining blocks until there is nothing left to rewind.

currentHeight = random.Next(currentHeight - 1) + 1; will always reduce currentHeight.

{
this.cachedCoinView.Rewind();
currentHeight = random.Next(currentHeight - 1) + 1;

this.cachedCoinView.Rewind(currentHeight);

uint256 currentTip = this.cachedCoinView.GetTipHash();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public override uint256 GetBlockHash()
throw new NotImplementedException();
}

public override Task<RewindState> RewindAsync()
public override Task<RewindState> RewindAsync(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ public void SaveChanges(IList<UnspentOutputs> unspentOutputs, IEnumerable<TxOut[
}

/// <inheritdoc />
public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
if (this.innerBlockHash == null)
{
Expand All @@ -481,7 +481,7 @@ public uint256 Rewind()

lock (this.lockobj)
{
uint256 hash = this.inner.Rewind();
uint256 hash = this.inner.Rewind(targetHeight);

foreach (KeyValuePair<uint256, CacheItem> cachedUtxoItem in this.cachedUtxoItems)
{
Expand All @@ -497,7 +497,7 @@ public uint256 Rewind()

this.innerBlockHash = hash;
this.blockHash = hash;
this.blockHeight -= 1;
this.blockHeight = targetHeight;

if (this.rewindDataIndexCache != null)
this.rewindDataIndexCache.Initialize(this.blockHeight, this);
Expand Down
3 changes: 2 additions & 1 deletion src/Stratis.Bitcoin.Features.Consensus/CoinViews/CoinView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ public interface ICoinView
/// and restoring recently spent outputs as UTXOs.
/// </para>
/// </summary>
/// <param name="targetHeight">The target height.</param>
/// <returns>Hash of the block header which is now the tip of the rewound coinview.</returns>
uint256 Rewind();
uint256 Rewind(int targetHeight);

/// <summary>
/// Gets the rewind data by block height.
Expand Down
63 changes: 47 additions & 16 deletions src/Stratis.Bitcoin.Features.Consensus/CoinViews/DBreezeCoinView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using DBreeze;
using DBreeze.DataTypes;
using DBreeze.Utils;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.BitcoinCore;
Expand Down Expand Up @@ -237,6 +238,7 @@ public void SaveChanges(IList<UnspentOutputs> unspentOutputs, IEnumerable<TxOut[
if (rewindDataList != null)
{
int nextRewindIndex = this.GetRewindIndex(transaction) + 1;

foreach (RewindData rewindData in rewindDataList)
{
this.logger.LogDebug("Rewind state #{0} created.", nextRewindIndex);
Expand Down Expand Up @@ -292,43 +294,72 @@ public RewindData GetRewindData(int height)
}

/// <inheritdoc />
public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
uint256 res = null;
using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
{
int currentHeight = this.GetRewindIndex(transaction);

Guard.Assert(targetHeight >= 0 && targetHeight < currentHeight);

transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
if (this.GetRewindIndex(transaction) == 0)

if (targetHeight == 0)
{
transaction.RemoveAllKeys("Coins", true);
this.SetBlockHash(transaction, this.network.GenesisHash);
transaction.RemoveAllKeys("Rewind", true);

res = this.network.GenesisHash;
}
else
{
transaction.ValuesLazyLoadingIsOn = false;

Row<int, byte[]> firstRow = transaction.SelectBackward<int, byte[]>("Rewind").FirstOrDefault();
transaction.RemoveKey("Rewind", firstRow.Key);
var rewindData = this.dBreezeSerializer.Deserialize<RewindData>(firstRow.Value);
this.SetBlockHash(transaction, rewindData.PreviousBlockHash);
var keysToRemove = new List<int>();
var changes = new Dictionary<uint256, Coins>();

foreach (uint256 txId in rewindData.TransactionsToRemove)
// Determine rewind data to remove up to but excluding the target height.
for (; currentHeight > targetHeight; currentHeight--)
{
this.logger.LogDebug("Outputs of transaction ID '{0}' will be removed.", txId);
transaction.RemoveKey("Coins", txId.ToBytes(false));
}
Row<int, byte[]> firstRow = transaction.Select<int, byte[]>("Rewind", currentHeight);
var rewindData = this.dBreezeSerializer.Deserialize<RewindData>(firstRow.Value);
keysToRemove.Add(firstRow.Key);

foreach (UnspentOutputs coin in rewindData.OutputsToRestore)
{
this.logger.LogDebug("Outputs of transaction ID '{0}' will be restored.", coin.TransactionId);
transaction.Insert("Coins", coin.TransactionId.ToBytes(false), this.dBreezeSerializer.Serialize(coin.ToCoins()));
foreach (uint256 txId in rewindData.TransactionsToRemove)
{
this.logger.LogTrace("Outputs of transaction ID '{0}' will be removed.", txId);
changes[txId] = null;
}

foreach (UnspentOutputs coin in rewindData.OutputsToRestore)
{
this.logger.LogTrace("Outputs of transaction ID '{0}' will be restored.", coin.TransactionId);
changes[coin.TransactionId] = coin.ToCoins();
}

res = rewindData.PreviousBlockHash;
}

res = rewindData.PreviousBlockHash;
var byteListComparer = new ByteListComparer();

// Remove the rewind data key.
foreach (int key in keysToRemove.OrderBy(k => k))
transaction.RemoveKey("Rewind", key);

// Remove coins where changes have null.
foreach (uint256 txId in changes.Where(x => x.Value == null).Select(x => x.Key).OrderBy(t => t.ToBytes(false), byteListComparer))
transaction.RemoveKey("Coins", txId.ToBytes(false));

// Add coins where changes contain the coins to add.
foreach (KeyValuePair<uint256, Coins> kv in changes.Where(x => x.Value != null).OrderBy(c => c.Key.ToBytes(false), byteListComparer))
transaction.Insert("Coins", kv.Key.ToBytes(false), this.dBreezeSerializer.Serialize(kv.Value));
}

// Set to hash of new tip.
this.SetBlockHash(transaction, res);

// Commit the changes.
transaction.Commit();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void SaveChanges(IList<UnspentOutputs> unspentOutputs, IEnumerable<TxOut[
}

/// <inheritdoc />
public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ public override uint256 GetBlockHash()
}

/// <inheritdoc />
public override Task<RewindState> RewindAsync()
public override Task<RewindState> RewindAsync(int targetHeight)
{
var state = new RewindState()
{
BlockHash = this.UtxoSet.Rewind()
BlockHash = this.UtxoSet.Rewind(targetHeight)
};

return Task.FromResult(state);
Expand All @@ -82,7 +82,7 @@ public override void Initialize(ChainedHeader chainTip)
this.logger.LogInformation("Rewinding coin db from {0}", consensusTipHash);
// In case block store initialized behind, rewind until or before the block store tip.
// The node will complete loading before connecting to peers so the chain will never know if a reorg happened.
consensusTipHash = breezeCoinView.Rewind();
consensusTipHash = breezeCoinView.Rewind(chainTip.Height);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Stratis.Bitcoin.Features.Consensus/StakeChainStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void Load()

while (currentHeader == null)
{
hash = this.dBreezeCoinView.Rewind();
hash = this.dBreezeCoinView.Rewind(this.chainIndexer.Height);
currentHeader = this.chainIndexer.GetHeader(hash);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Stratis.Bitcoin.Features.MemoryPool/MemPoolCoinView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void SaveChanges(IList<UnspentOutputs> unspentOutputs, IEnumerable<TxOut[
}

/// <inheritdoc />
public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down
3 changes: 1 addition & 2 deletions src/Stratis.Bitcoin.IntegrationTests/CoinViewTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ public uint256 NewBlock()

public uint256 Rewind()
{
this.hash = this.coinView.Rewind();
this.blockHeight--;
this.hash = this.coinView.Rewind(--this.blockHeight);
Copy link
Collaborator

@codingupastorm codingupastorm Nov 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? This is different functionality to previously. This will pass blockHeight-1 to Rewind.

Copy link
Contributor Author

@quantumagi quantumagi Nov 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's the target height. Previously we implicitly passed blockHeight-1 to Rewind. Now its explicit.

return this.hash;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void SaveChanges(IList<UnspentOutputs> unspentOutputs, IEnumerable<TxOut[
}

/// <inheritdoc />
public uint256 Rewind()
public uint256 Rewind(int targetHeight)
{
throw new NotImplementedException();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Stratis.Bitcoin/Consensus/ConsensusManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public async Task InitializeAsync(ChainedHeader chainTip)

// In case block store initialized behind, rewind until or before the block store tip.
// The node will complete loading before connecting to peers so the chain will never know if a reorg happened.
RewindState transitionState = await this.ConsensusRules.RewindAsync().ConfigureAwait(false);
RewindState transitionState = await this.ConsensusRules.RewindAsync(this.chainState.BlockStoreTip.Height).ConfigureAwait(false);
consensusTipHash = transitionState.BlockHash;
}

Expand Down Expand Up @@ -724,7 +724,7 @@ private async Task<List<ChainedHeaderBlock>> RewindToForkPointAsync(ChainedHeade
}
}

await this.ConsensusRules.RewindAsync().ConfigureAwait(false);
await this.ConsensusRules.RewindAsync(current.Previous.Height).ConfigureAwait(false);

lock (this.peerLock)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Stratis.Bitcoin/Consensus/ConsensusRuleEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private void ExecuteRules(IEnumerable<SyncConsensusRule> rules, RuleContext rule
public abstract uint256 GetBlockHash();

/// <inheritdoc />
public abstract Task<RewindState> RewindAsync();
public abstract Task<RewindState> RewindAsync(int targetHeight);

[NoTrace]
public T GetRule<T>() where T : ConsensusRuleBase
Expand Down
3 changes: 2 additions & 1 deletion src/Stratis.Bitcoin/Consensus/IConsensusRuleEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ public interface IConsensusRuleEngine : IDisposable
/// and restoring the chain to an earlier state.
/// </para>
/// </summary>
/// <param name="targetHeight">The target height.</param>
/// <returns>Hash of the block header which is now the tip of the chain.</returns>
Task<RewindState> RewindAsync();
Task<RewindState> RewindAsync(int targetHeight);

/// <summary>Execute header validation rules.</summary>
/// <param name="header">The chained header that is going to be validated.</param>
Expand Down