BlockchainState start
MODIFY
WalletWasabi/KeyManagement/KeyManager.cs
WalletWasabi/KeyManagement/KeyManager.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using System.Security;
using System;
+using WalletWasabi.Models;
namespace WalletWasabi.KeyManagement
{
@@ -30,17 +31,22 @@ namespace WalletWasabi.KeyManagement
public bool? PasswordVerified { get; private set; }
[JsonProperty(Order = 5)]
+ private BlockchainState BlockchainState { get; }
+
+ private object BlockchainStateLock { get; }
+
+ [JsonProperty(Order = 6)]
private List<HdPubKey> HdPubKeys { get; }
- private readonly object HdPubKeysLock;
+ private object HdPubKeysLock { get; }
private List<byte[]> HdPubKeyScriptBytes { get; }
- private readonly object HdPubKeyScriptBytesLock;
+ private object HdPubKeyScriptBytesLock { get; }
- private readonly Dictionary<Script, HdPubKey> ScriptHdPubkeyMap;
+ private Dictionary<Script, HdPubKey> ScriptHdPubkeyMap { get; }
- private readonly object ScriptHdPubkeyMapLock;
+ private object ScriptHdPubkeyMapLock { get; }
// BIP84-ish derivation scheme
// m / purpose' / coin_type' / account' / change / address_index
@@ -51,7 +57,7 @@ namespace WalletWasabi.KeyManagement
private object ToFileLock { get; }
[JsonConstructor]
- public KeyManager(BitcoinEncryptedSecretNoEC encryptedSecret, byte[] chainCode, ExtPubKey extPubKey, bool? passwordVerified, string filePath = null)
+ public KeyManager(BitcoinEncryptedSecretNoEC encryptedSecret, byte[] chainCode, ExtPubKey extPubKey, bool? passwordVerified, BlockchainState blockchainState, string filePath = null)
{
HdPubKeys = new List<HdPubKey>();
HdPubKeyScriptBytes = new List<byte[]>();
@@ -59,6 +65,7 @@ namespace WalletWasabi.KeyManagement
HdPubKeysLock = new object();
HdPubKeyScriptBytesLock = new object();
ScriptHdPubkeyMapLock = new object();
+ BlockchainStateLock = new object();
EncryptedSecret = Guard.NotNull(nameof(encryptedSecret), encryptedSecret);
ChainCode = Guard.NotNull(nameof(chainCode), chainCode);
@@ -66,6 +73,8 @@ namespace WalletWasabi.KeyManagement
PasswordVerified = passwordVerified;
+ BlockchainState = blockchainState ?? new BlockchainState();
+
SetFilePath(filePath);
ToFileLock = new object();
ToFile();
@@ -79,6 +88,8 @@ namespace WalletWasabi.KeyManagement
HdPubKeysLock = new object();
HdPubKeyScriptBytesLock = new object();
ScriptHdPubkeyMapLock = new object();
+ BlockchainState = new BlockchainState();
+ BlockchainStateLock = new object();
if (password is null)
{
@@ -107,7 +118,7 @@ namespace WalletWasabi.KeyManagement
ExtKey extKey = mnemonic.DeriveExtKey(password);
var encryptedSecret = extKey.PrivateKey.GetEncryptedBitcoinSecret(password, Network.Main);
- return new KeyManager(encryptedSecret, extKey.ChainCode, extKey.Derive(AccountKeyPath).Neuter(), false, filePath);
+ return new KeyManager(encryptedSecret, extKey.ChainCode, extKey.Derive(AccountKeyPath).Neuter(), false, new BlockchainState(), filePath);
}
public static KeyManager Recover(Mnemonic mnemonic, string password, string filePath = null)
@@ -121,7 +132,7 @@ namespace WalletWasabi.KeyManagement
ExtKey extKey = mnemonic.DeriveExtKey(password);
var encryptedSecret = extKey.PrivateKey.GetEncryptedBitcoinSecret(password, Network.Main);
- return new KeyManager(encryptedSecret, extKey.ChainCode, extKey.Derive(AccountKeyPath).Neuter(), true, filePath);
+ return new KeyManager(encryptedSecret, extKey.ChainCode, extKey.Derive(AccountKeyPath).Neuter(), true, new BlockchainState(), filePath);
}
public void SetFilePath(string filePath)
@@ -141,12 +152,19 @@ namespace WalletWasabi.KeyManagement
{
IoHelpers.EnsureContainingDirectoryExists(filePath);
lock (HdPubKeysLock)
- {
+ lock (BlockchainStateLock)
lock (ToFileLock)
{
+ // Remove the last 100 blocks to ensure verification on the next run. This is needed of reorg.
+ var matureHeight = BlockchainState.LastBlockHeight - 101;
+ var toRemove = BlockchainState.BlockStates.Where(x => x.BlockHeight >= matureHeight).ToHashSet();
+ BlockchainState.BlockStates.RemoveAll(x => toRemove.Contains(x));
+
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(filePath, jsonString, Encoding.UTF8);
- }
+
+ // Re-add removed items for further operations.
+ BlockchainState.BlockStates.AddRange(toRemove.OrderBy(x => x));
}
}
@@ -256,6 +274,22 @@ namespace WalletWasabi.KeyManagement
}
}
+ public Height GetBestHeight()
+ {
+ lock (BlockchainStateLock)
+ {
+ return BlockchainState.LastBlockHeight;
+ }
+ }
+
+ internal IEnumerable<BlockState> GetTransactionIndex()
+ {
+ lock (BlockchainStateLock)
+ {
+ return BlockchainState.BlockStates.OrderBy(x => x).ToList();
+ }
+ }
+
public void SetPasswordVerified()
{
PasswordVerified = true;

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -262,8 +262,17 @@ namespace WalletWasabi.Services
using (HandleFiltersLock.Lock())
using (WalletBlocksLock.Lock())
{
+ // Go through the keymanager's index.
+ var bestKeyManagerHeight = KeyManager.GetBestHeight();
+ foreach (var blockstate in KeyManager.GetTransactionIndex())
+ {
+ var block = await GetOrDownloadBlockAsync(blockstate.BlockHash, cancel);
+ WalletBlocks.AddOrReplace(blockstate.BlockHeight, blockstate.BlockHash);
+
+ ProcessBlock(blockstate.BlockHeight, block, blockstate.TransactionIndices);
+ }
// Go through the filters and que to download the matches.
- foreach (FilterModel filterModel in Synchronizer.GetFilters().Where(x => !(x.Filter is null) && !WalletBlocks.ContainsValue(x.BlockHash))) // Filter can be null if there is no bech32 tx.
+ foreach (FilterModel filterModel in Synchronizer.GetFilters().Where(x => !(x.Filter is null) && x.BlockHeight >= bestKeyManagerHeight && !WalletBlocks.ContainsValue(x.BlockHash))) // Filter can be null if there is no bech32 tx.
{
await ProcessFilterModelAsync(filterModel, cancel);
}
@@ -465,12 +474,23 @@ namespace WalletWasabi.Services
return clusters;
}
- private void ProcessBlock(Height height, Block block)
+ private void ProcessBlock(Height height, Block block, IEnumerable<int> filterByTxIds = null)
+ {
+ if (filterByTxIds is null)
{
foreach (var tx in block.Transactions)
{
ProcessTransaction(new SmartTransaction(tx, height));
}
+ }
+ else
+ {
+ foreach (var i in filterByTxIds.OrderBy(x => x))
+ {
+ Transaction tx = block.Transactions[i];
+ ProcessTransaction(new SmartTransaction(tx, height));
+ }
+ }
ProcessedBlocks.TryAdd(block.GetHash(), (height, block.Header.BlockTime));

BlockchainState start