Exemple #1
0
        protected bool TryGetStorageValue(TKey key, out TValue value, bool saveInCache)
        {
            TValue valueLocal = default(TValue);
            var    result     = new MethodTimer(false && this.Name == "BlockCache").Time(() =>
            {
                TValue storedValue;
                if (dataStorage.TryReadValue(key, out storedValue))
                {
                    // cache the retrieved value
                    if (saveInCache)
                    {
                        CacheValue(key, storedValue);
                    }

                    // fire retrieved event
                    var handler = this.OnRetrieved;
                    if (handler != null)
                    {
                        handler(key, storedValue);
                    }

                    valueLocal = storedValue;
                    return(true);
                }
                else
                {
                    valueLocal = default(TValue);
                    return(false);
                }
            });

            value = valueLocal;
            return(result);
        }
Exemple #2
0
        private IEnumerable <Tuple <int, ChainedBlock> > ChainedBlockLookAhead(IEnumerable <Tuple <int, ChainedHeader> > chain, int lookAhead)
        {
            return(chain
                   .Select(
                       chainedHeaderTuple =>
            {
                try
                {
                    // cooperative loop
                    this.ThrowIfCancelled();

                    var direction = chainedHeaderTuple.Item1;

                    var chainedHeader = chainedHeaderTuple.Item2;
                    var block = new MethodTimer(false).Time("GetBlock", () =>
                                                            this.blockCache[chainedHeader.Hash]);

                    var chainedBlock = new ChainedBlock(chainedHeader, block);

                    return Tuple.Create(direction, chainedBlock);
                }
                catch (MissingDataException e)
                {
                    this.logger.Debug("Stalled, MissingDataException: {0}".Format2(e.Key));
                    throw;
                }
            })
                   .LookAhead(1));
        }
Exemple #3
0
    static void Main(string[] args)
    {
        long time = MethodTimer.Run(() => File.Open(@"c:\test.txt",
                                                    FileMode.CreateNew));

        Console.WriteLine(time);
        Console.ReadLine();
    }
Exemple #4
0
        public void SuccessRate(int iterations, double successRate)
        {
            var fickleTimes = MethodTimer.MeasureExecution(() => FickleFunc(successRate), iterations);

            Asserter.Against(fickleTimes)
            .And(Has.Property(nameof(fickleTimes.SuccessRate)).CloseTo(successRate, 0.005))
            .And(Has.Property(nameof(fickleTimes.Iterations)).EqualTo(iterations))
            .Invoke();
        }
Exemple #5
0
        private void UpdateTargetChain()
        {
            try
            {
                long origChanged;
                lock (this.changedLock)
                    origChanged = this.changed;

                var targetBlockLocal = this.targetBlock;
                var targetChainLocal = this.targetChain;

                if (targetBlockLocal != null &&
                    (targetChainLocal == null || targetBlockLocal.Hash != targetChainLocal.LastBlock.Hash))
                {
                    var newTargetChain =
                        targetChainLocal != null
                            ? targetChainLocal.ToBuilder()
                            : new ChainBuilder(Chain.CreateForGenesisBlock(this.rules.GenesisChainedHeader));

                    var deltaBlockPath = new MethodTimer(false).Time("deltaBlockPath", () =>
                                                                     new BlockchainWalker().GetBlockchainPath(newTargetChain.LastBlock, targetBlockLocal, blockHash => this.coreStorage.GetChainedHeader(blockHash)));

                    foreach (var rewindBlock in deltaBlockPath.RewindBlocks)
                    {
                        newTargetChain.RemoveBlock(rewindBlock);
                    }

                    foreach (var advanceBlock in deltaBlockPath.AdvanceBlocks)
                    {
                        newTargetChain.AddBlock(advanceBlock);
                    }

                    this.logger.Debug("Winning chained block {0} at height {1}, total work: {2}".Format2(newTargetChain.LastBlock.Hash.ToHexNumberString(), newTargetChain.Height, newTargetChain.LastBlock.TotalWork.ToString("X")));
                    this.targetChain = newTargetChain.ToImmutable();

                    var handler = this.OnTargetChainChanged;
                    if (handler != null)
                    {
                        handler();
                    }
                }

                lock (this.changedLock)
                {
                    if (this.changed == origChanged)
                    {
                        this.updatedEvent.Set();
                    }
                    else
                    {
                        this.NotifyWork();
                    }
                }
            }
            catch (MissingDataException) { }
        }
        public Tuple<ECPrivateKeyParameters, ECPublicKeyParameters> CreateKeyPair()
        {
            var curve = SecNamedCurves.GetByName("secp256k1");
            var domainParameters = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());

            var generator = new ECKeyPairGenerator();
            generator.Init(new ECKeyGenerationParameters(domainParameters, new SecureRandom()));
            var keyPair = new MethodTimer(false).Time("GenerateKeyPair", () => generator.GenerateKeyPair());

            var privateKey = (ECPrivateKeyParameters)keyPair.Private;
            var publicKey = (ECPublicKeyParameters)keyPair.Public;

            //Debug.WriteLine("Private:  {0}".Format2(privateKey.D.ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Public X: {0}".Format2(publicKey.Q.X.ToBigInteger().ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Public Y: {0}".Format2(publicKey.Q.Y.ToBigInteger().ToHexNumberStringUnsigned()));

            return Tuple.Create(privateKey, publicKey);
        }
        public Tuple <ECPrivateKeyParameters, ECPublicKeyParameters> CreateKeyPair()
        {
            var curve            = SecNamedCurves.GetByName("secp256k1");
            var domainParameters = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());

            var generator = new ECKeyPairGenerator();

            generator.Init(new ECKeyGenerationParameters(domainParameters, new SecureRandom()));
            var keyPair = new MethodTimer(false).Time("GenerateKeyPair", () => generator.GenerateKeyPair());

            var privateKey = (ECPrivateKeyParameters)keyPair.Private;
            var publicKey  = (ECPublicKeyParameters)keyPair.Public;

            //Debug.WriteLine("Private:  {0}".Format2(privateKey.D.ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Public X: {0}".Format2(publicKey.Q.X.ToBigInteger().ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Public Y: {0}".Format2(publicKey.Q.Y.ToBigInteger().ToHexNumberStringUnsigned()));

            return(Tuple.Create(privateKey, publicKey));
        }
Exemple #8
0
        public void ListVsSet()
        {
            const int iterations = 1000;
            var       items      = MakeHugeCollection(iterations).ToArray();
            var       toFind     = items.Random();

            // Using their respecting .Contains() methods

            var list = items.ToList();
            var set  = items.ToHashSet();

            var listTimes = MethodTimer.MeasureExecution(() => ListContains(list, toFind), iterations);
            var setTimes  = MethodTimer.MeasureExecution(() => SetContains(set, toFind), iterations);

            Console.WriteLine($"list: {listTimes}");
            Console.WriteLine($"set:  {setTimes}");

            // Using the extension Enumerable.Contains()

            list.RandomizeEntries();
            set.RandomizeEntries();

            var listableTimes = MethodTimer.MeasureExecution(() => EnumerableContains(list, toFind), iterations);
            var settableTimes = MethodTimer.MeasureExecution(() => EnumerableContains(set, toFind), iterations);

            Console.WriteLine($"{nameof(listableTimes)}: {listableTimes}");
            Console.WriteLine($"{nameof(settableTimes)}: {settableTimes}");

            // Actually doing a comparison
            var comparison = new AggregateExecutionComparison(listTimes, setTimes);

            Console.WriteLine(comparison);
            Asserter.Against(comparison)
            .And(AssertComparison(comparison.Average, 1))
            .And(AssertComparison(comparison.Total, 1))
            .Invoke();
        }
        public IEnumerable <Tuple <Block, ChainedBlock /*, ImmutableDictionary<UInt256, Transaction>*/> > BlockAndTxLookAhead(IList <UInt256> blockHashes)
        {
            var blockLookAhead = LookAheadMethods.LookAhead(
                () => blockHashes.Select(
                    blockHash =>
            {
                var block = new MethodTimer(false).Time("GetBlock", () =>
                                                        this.CacheContext.GetBlock(blockHash, saveInCache: false));

                this.CacheContext.TransactionCache.CacheBlock(block);

                //var transactionsBuilder = ImmutableDictionary.CreateBuilder<UInt256, Transaction>();
                //var inputTxHashList = block.Transactions.Skip(1).SelectMany(x => x.Inputs).Select(x => x.PreviousTxOutputKey.TxHash).Distinct();

                //// pre-cache input transactions
                ////Parallel.ForEach(inputTxHashList, inputTxHash =>
                //foreach (var inputTxHash in inputTxHashList)
                //{
                //    Transaction inputTx;
                //    if (this.CacheContext.TransactionCache.TryGetValue(inputTxHash, out inputTx, saveInCache: false))
                //    {
                //        transactionsBuilder.Add(inputTxHash, inputTx);
                //    }
                //}

                //return Tuple.Create(block, transactionsBuilder.ToImmutable());
                return(block);
            }),
                this.shutdownToken);

            var chainedBlockLookAhead = LookAheadMethods.LookAhead(
                () => blockHashes.Select(blockHash => this.CacheContext.GetChainedBlock(blockHash, saveInCache: false)),
                this.shutdownToken);

            return(blockLookAhead.Zip(chainedBlockLookAhead, (block, chainedBlock) => Tuple.Create(block, chainedBlock)));
        }
        public IEnumerable<Tuple<Block, ChainedBlock /*, ImmutableDictionary<UInt256, Transaction>*/>> BlockAndTxLookAhead(IList<UInt256> blockHashes)
        {
            var blockLookAhead = LookAheadMethods.LookAhead(
                () => blockHashes.Select(
                    blockHash =>
                    {
                        var block = new MethodTimer(false).Time("GetBlock", () =>
                            this.CacheContext.GetBlock(blockHash, saveInCache: false));

                        this.CacheContext.TransactionCache.CacheBlock(block);

                        //var transactionsBuilder = ImmutableDictionary.CreateBuilder<UInt256, Transaction>();
                        //var inputTxHashList = block.Transactions.Skip(1).SelectMany(x => x.Inputs).Select(x => x.PreviousTxOutputKey.TxHash).Distinct();

                        //// pre-cache input transactions
                        ////Parallel.ForEach(inputTxHashList, inputTxHash =>
                        //foreach (var inputTxHash in inputTxHashList)
                        //{
                        //    Transaction inputTx;
                        //    if (this.CacheContext.TransactionCache.TryGetValue(inputTxHash, out inputTx, saveInCache: false))
                        //    {
                        //        transactionsBuilder.Add(inputTxHash, inputTx);
                        //    }
                        //}

                        //return Tuple.Create(block, transactionsBuilder.ToImmutable());
                        return block;
                    }),
                this.shutdownToken);

            var chainedBlockLookAhead = LookAheadMethods.LookAhead(
                () => blockHashes.Select(blockHash => this.CacheContext.GetChainedBlock(blockHash, saveInCache: false)),
                this.shutdownToken);

            return blockLookAhead.Zip(chainedBlockLookAhead, (block, chainedBlock) => Tuple.Create(block, chainedBlock));
        }
        public Data.Blockchain CalculateBlockchainFromExisting(Data.Blockchain currentBlockchain, ChainedBlock targetChainedBlock, out List<MissingDataException> missingData, CancellationToken cancelToken, Action<Data.Blockchain> onProgress = null)
        {
            Debug.WriteLine("Winning chained block {0} at height {1}, total work: {2}".Format2(targetChainedBlock.BlockHash.ToHexNumberString(), targetChainedBlock.Height, targetChainedBlock.TotalWork.ToString("X")));

            // if the target block is at height 0 don't use currentBlockchain as-is, set it to be the genesis chain for the target block
            if (targetChainedBlock.Height == 0)
            {
                currentBlockchain = new Data.Blockchain
                (
                    blockList: ImmutableList.Create(targetChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(targetChainedBlock.BlockHash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>()
                );
            }
            // if currentBlockchain is not present find the genesis block for the target block and use it as the current chain
            else if (currentBlockchain.IsDefault)
            {
                // find the genesis block for the target block
                var genesisBlock = targetChainedBlock;
                foreach (var prevBlock in PreviousChainedBlocks(targetChainedBlock))
                {
                    // cooperative loop
                    this.shutdownToken.ThrowIfCancellationRequested();
                    cancelToken.ThrowIfCancellationRequested();

                    genesisBlock = prevBlock;
                }

                currentBlockchain = new Data.Blockchain
                (
                    blockList: ImmutableList.Create(genesisBlock),
                    blockListHashes: ImmutableHashSet.Create(genesisBlock.BlockHash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>()
                );
            }

            missingData = new List<MissingDataException>();

            Debug.WriteLine("Searching for last common ancestor between current chainblock and winning chainblock");

            List<UInt256> newChainBlockList;
            var lastCommonAncestorChain = RollbackToLastCommonAncestor(currentBlockchain, targetChainedBlock, cancelToken, out newChainBlockList);

            Debug.WriteLine("Last common ancestor found at block {0}, height {1:#,##0}, begin processing winning blockchain".Format2(currentBlockchain.RootBlockHash.ToHexNumberString(), currentBlockchain.Height));

            // setup statistics
            var totalTxCount = 0L;
            var totalInputCount = 0L;
            var totalStopwatch = new Stopwatch();

            var currentBlockCount = 0L;
            var currentTxCount = 0L;
            var currentInputCount = 0L;
            var currentRateStopwatch = new Stopwatch();

            totalStopwatch.Start();
            currentRateStopwatch.Start();

            // with last common ancestor found and utxo rolled back to that point, calculate the new blockchain
            // use ImmutableList for BlockList during modification
            var newBlockchain = new Data.Blockchain
            (
                blockList: lastCommonAncestorChain.BlockList,
                blockListHashes: lastCommonAncestorChain.BlockListHashes,
                utxo: lastCommonAncestorChain.Utxo
            );

            // start calculating new utxo
            foreach (var tuple in BlockAndTxLookAhead(newChainBlockList))
            {
                // cooperative loop
                this.shutdownToken.ThrowIfCancellationRequested();
                cancelToken.ThrowIfCancellationRequested();

                try
                {
                    // get block and metadata for next link in blockchain
                    var nextBlock = tuple.Item1;
                    var nextChainedBlock = tuple.Item2;

                    // calculate the new block utxo, double spends will be checked for
                    ImmutableDictionary<UInt256, ImmutableHashSet<int>> newTransactions = ImmutableDictionary.Create<UInt256, ImmutableHashSet<int>>();
                    long txCount = 0, inputCount = 0;
                    var newUtxo = new MethodTimer(false).Time("CalculateUtxo", () =>
                        CalculateUtxo(nextChainedBlock.Height, nextBlock, newBlockchain.Utxo, out newTransactions, out txCount, out inputCount));

                    var nextBlockchain =
                        new MethodTimer(false).Time("nextBlockchain", () =>
                            new Data.Blockchain
                            (
                                blockList: newBlockchain.BlockList.Add(nextChainedBlock),
                                blockListHashes: newBlockchain.BlockListHashes.Add(nextChainedBlock.BlockHash),
                                utxo: newUtxo
                            ));

                    // validate the block
                    // validation utxo includes all transactions added in the same block, any double spends will have failed the block above
                    validateStopwatch.Start();
                    new MethodTimer(false).Time("ValidateBlock", () =>
                        this.Rules.ValidateBlock(nextBlock, nextBlockchain, newBlockchain.Utxo, newTransactions));
                    validateStopwatch.Stop();

                    // create the next link in the new blockchain
                    newBlockchain = nextBlockchain;
                    if (onProgress != null)
                        onProgress(newBlockchain);

                    // blockchain processing statistics
                    currentBlockCount++;
                    currentTxCount += txCount;
                    currentInputCount += inputCount;
                    totalTxCount += txCount;
                    totalInputCount += inputCount;

                    var txInterval = 100.THOUSAND();
                    if (
                        newBlockchain.Height % 10.THOUSAND() == 0
                        || (totalTxCount % txInterval < (totalTxCount - txCount) % txInterval || txCount >= txInterval))
                    {
                        LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

                        currentBlockCount = 0;
                        currentTxCount = 0;
                        currentInputCount = 0;
                        currentRateStopwatch.Reset();
                        currentRateStopwatch.Start();
                    }
                }
                catch (MissingDataException e)
                {
                    // if there is missing data once blockchain processing has started, return the current progress
                    missingData.Add(e);
                    break;
                }
                catch (AggregateException e)
                {
                    if (e.InnerExceptions.Any(x => !(x is MissingDataException)))
                    {
                        throw;
                    }
                    else
                    {
                        missingData.AddRange(e.InnerExceptions.OfType<MissingDataException>());
                        break;
                    }
                }
            }

            if (onProgress != null)
                onProgress(newBlockchain);

            LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

            return newBlockchain;
        }
        public Data.Blockchain CalculateBlockchainFromExisting(Data.Blockchain currentBlockchain, ChainedBlock targetChainedBlock, out List <MissingDataException> missingData, CancellationToken cancelToken, Action <Data.Blockchain> onProgress = null)
        {
            Debug.WriteLine("Winning chained block {0} at height {1}, total work: {2}".Format2(targetChainedBlock.BlockHash.ToHexNumberString(), targetChainedBlock.Height, targetChainedBlock.TotalWork.ToString("X")));

            // if the target block is at height 0 don't use currentBlockchain as-is, set it to be the genesis chain for the target block
            if (targetChainedBlock.Height == 0)
            {
                currentBlockchain = new Data.Blockchain
                                    (
                    blockList: ImmutableList.Create(targetChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(targetChainedBlock.BlockHash),
                    utxo: ImmutableDictionary.Create <UInt256, UnspentTx>()
                                    );
            }
            // if currentBlockchain is not present find the genesis block for the target block and use it as the current chain
            else if (currentBlockchain == null)
            {
                // find the genesis block for the target block
                var genesisBlock = targetChainedBlock;
                foreach (var prevBlock in PreviousChainedBlocks(targetChainedBlock))
                {
                    // cooperative loop
                    this.shutdownToken.ThrowIfCancellationRequested();
                    cancelToken.ThrowIfCancellationRequested();

                    genesisBlock = prevBlock;
                }

                currentBlockchain = new Data.Blockchain
                                    (
                    blockList: ImmutableList.Create(genesisBlock),
                    blockListHashes: ImmutableHashSet.Create(genesisBlock.BlockHash),
                    utxo: ImmutableDictionary.Create <UInt256, UnspentTx>()
                                    );
            }

            missingData = new List <MissingDataException>();

            Debug.WriteLine("Searching for last common ancestor between current chainblock and winning chainblock");

            List <UInt256> newChainBlockList;
            var            lastCommonAncestorChain = RollbackToLastCommonAncestor(currentBlockchain, targetChainedBlock, cancelToken, out newChainBlockList);

            Debug.WriteLine("Last common ancestor found at block {0}, height {1:#,##0}, begin processing winning blockchain".Format2(currentBlockchain.RootBlockHash.ToHexNumberString(), currentBlockchain.Height));

            // setup statistics
            var totalTxCount    = 0L;
            var totalInputCount = 0L;
            var totalStopwatch  = new Stopwatch();

            var currentBlockCount    = 0L;
            var currentTxCount       = 0L;
            var currentInputCount    = 0L;
            var currentRateStopwatch = new Stopwatch();

            totalStopwatch.Start();
            currentRateStopwatch.Start();

            // with last common ancestor found and utxo rolled back to that point, calculate the new blockchain
            // use ImmutableList for BlockList during modification
            var newBlockchain = new Data.Blockchain
                                (
                blockList: lastCommonAncestorChain.BlockList,
                blockListHashes: lastCommonAncestorChain.BlockListHashes,
                utxo: lastCommonAncestorChain.Utxo
                                );

            // start calculating new utxo
            foreach (var tuple in BlockAndTxLookAhead(newChainBlockList))
            {
                // cooperative loop
                this.shutdownToken.ThrowIfCancellationRequested();
                cancelToken.ThrowIfCancellationRequested();

                try
                {
                    // get block and metadata for next link in blockchain
                    var nextBlock        = tuple.Item1;
                    var nextChainedBlock = tuple.Item2;

                    // calculate the new block utxo, double spends will be checked for
                    ImmutableDictionary <UInt256, ImmutableHashSet <int> > newTransactions = ImmutableDictionary.Create <UInt256, ImmutableHashSet <int> >();
                    long txCount = 0, inputCount = 0;
                    var  newUtxo = new MethodTimer(false).Time("CalculateUtxo", () =>
                                                               CalculateUtxo(nextChainedBlock.Height, nextBlock, newBlockchain.Utxo, out newTransactions, out txCount, out inputCount));

                    var nextBlockchain =
                        new MethodTimer(false).Time("nextBlockchain", () =>
                                                    new Data.Blockchain
                                                    (
                                                        blockList: newBlockchain.BlockList.Add(nextChainedBlock),
                                                        blockListHashes: newBlockchain.BlockListHashes.Add(nextChainedBlock.BlockHash),
                                                        utxo: newUtxo
                                                    ));

                    // validate the block
                    // validation utxo includes all transactions added in the same block, any double spends will have failed the block above
                    validateStopwatch.Start();
                    new MethodTimer(false).Time("ValidateBlock", () =>
                                                this.Rules.ValidateBlock(nextBlock, nextBlockchain, newBlockchain.Utxo, newTransactions));
                    validateStopwatch.Stop();

                    // create the next link in the new blockchain
                    newBlockchain = nextBlockchain;
                    if (onProgress != null)
                    {
                        onProgress(newBlockchain);
                    }

                    // blockchain processing statistics
                    currentBlockCount++;
                    currentTxCount    += txCount;
                    currentInputCount += inputCount;
                    totalTxCount      += txCount;
                    totalInputCount   += inputCount;

                    var txInterval = 100.THOUSAND();
                    if (
                        newBlockchain.Height % 10.THOUSAND() == 0 ||
                        (totalTxCount % txInterval < (totalTxCount - txCount) % txInterval || txCount >= txInterval))
                    {
                        LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

                        currentBlockCount = 0;
                        currentTxCount    = 0;
                        currentInputCount = 0;
                        currentRateStopwatch.Reset();
                        currentRateStopwatch.Start();
                    }
                }
                catch (MissingDataException e)
                {
                    // if there is missing data once blockchain processing has started, return the current progress
                    missingData.Add(e);
                    break;
                }
                catch (AggregateException e)
                {
                    if (e.InnerExceptions.Any(x => !(x is MissingDataException)))
                    {
                        throw;
                    }
                    else
                    {
                        missingData.AddRange(e.InnerExceptions.OfType <MissingDataException>());
                        break;
                    }
                }
            }

            if (onProgress != null)
            {
                onProgress(newBlockchain);
            }

            LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

            return(newBlockchain);
        }
Exemple #13
0
        private bool WorkActionInner()
        {
            var targetBlockLocal = this.targetBlockWorker.TargetBlock;

            Chain targetChainLocal;

            if (this.rescanEvent.WaitOne(0))
            {
                targetChainLocal = null;
            }
            else
            {
                targetChainLocal = this.targetChain;
            }

            if (targetBlockLocal != null &&
                (targetChainLocal == null || targetBlockLocal.Hash != targetChainLocal.LastBlock.Hash))
            {
                var newTargetChain =
                    targetChainLocal != null
                    ? targetChainLocal.ToBuilder()
                    : new ChainBuilder(Chain.CreateForGenesisBlock(this.rules.GenesisChainedHeader));

                var deltaBlockPath = new MethodTimer(false).Time("deltaBlockPath", () =>
                                                                 new BlockchainWalker().GetBlockchainPath(newTargetChain.LastBlock, targetBlockLocal, blockHash => this.chainedHeaderCache[blockHash]));

                foreach (var rewindBlock in deltaBlockPath.RewindBlocks)
                {
                    if (this.invalidBlockCache.ContainsKey(rewindBlock.Hash))
                    {
                        this.rescanEvent.Set();
                        return(true);
                    }

                    newTargetChain.RemoveBlock(rewindBlock);
                }

                var invalid = false;
                foreach (var advanceBlock in deltaBlockPath.AdvanceBlocks)
                {
                    if (this.invalidBlockCache.ContainsKey(advanceBlock.Hash))
                    {
                        invalid = true;
                    }

                    if (!invalid)
                    {
                        newTargetChain.AddBlock(advanceBlock);
                    }
                    else
                    {
                        this.invalidBlockCache.TryAdd(advanceBlock.Hash, "");
                    }
                }

                this.logger.Debug("Winning chained block {0} at height {1}, total work: {2}".Format2(newTargetChain.LastBlock.Hash.ToHexNumberString(), newTargetChain.Height, newTargetChain.LastBlock.TotalWork.ToString("X")));
                this.targetChain = newTargetChain.ToImmutable();

                var handler = this.OnTargetChainChanged;
                if (handler != null)
                {
                    handler();
                }
            }

            return(false);
        }