/// <summary>
        ///     Creates and store a <see cref="ProvenBlockHeader" /> generated by the signaled <see cref="ChainedHeaderBlock" />.
        /// </summary>
        /// <param name="blockHeight">Height of the block used to generate its Proven Header.</param>
        /// <param name="chainedHeaderBlock">Block used to generate its Proven Header.</param>
        /// <param name="isIBD">Is node in IBD.</param>
        void CreateAndStoreProvenHeader(int blockHeight, ChainedHeaderBlock chainedHeaderBlock, bool isIBD)
        {
            var block = (PosBlock)chainedHeaderBlock.Block;

            var newProvenHeader =
                ((PosConsensusFactory)this.network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

            var provenHeaderHash = newProvenHeader.GetHash();

            this.provenBlockHeaderStore.AddToPendingBatch(newProvenHeader,
                                                          new HashHeightPair(provenHeaderHash, blockHeight));

            this.logger.LogDebug(
                "Created Proven Header at height {0} with hash {1} and adding to the pending batch to be stored.",
                blockHeight, provenHeaderHash);

            // If our node is in IBD the block will not be announced to peers.
            // If not in IBD the signaler may expect the block header to be of type PH.
            // TODO: Memory foot print:
            // This design will cause memory to grow over time (depending on how long the node is running)
            // based on the size of the Proven Headers (a proven header can be up to 1000 bytes).
            // This is also correct for regular header (which are 80 bytes in size).
            // If we want to be able to control the size of PH we will need to change the logic
            // in ProvenHeadersBlockStoreBehavior and load the PH from the PH store instead
            if (!isIBD)
            {
                chainedHeaderBlock.SetHeader(newProvenHeader);
            }
        }
Esempio n. 2
0
        private async Task OnPartialValidationCompletedCallbackAsync(ChainedHeaderBlock chainedHeaderBlock, bool success)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(chainedHeaderBlock), chainedHeaderBlock, nameof(success), success);

            if (success)
            {
                await this.OnPartialValidationSucceededAsync(chainedHeaderBlock).ConfigureAwait(false);
            }
            else
            {
                List <int> peersToBan;

                lock (this.peerLock)
                {
                    peersToBan = this.chainedHeaderTree.PartialOrFullValidationFailed(chainedHeaderBlock.ChainedHeader);
                }

                this.logger.LogDebug("Validation of block '{0}' failed, banning and disconnecting {1} peers.", chainedHeaderBlock, peersToBan.Count);

                foreach (int peerId in peersToBan)
                {
                    // TODO: ban and disconnect those peers
                }
            }

            this.logger.LogTrace("(-)");
        }
Esempio n. 3
0
        private void OnBlockConnected(BlockConnected blockConnected)
        {
            ChainedHeaderBlock blockPair = blockConnected.ConnectedBlock;

            ChainedHeader chainedHeader = blockPair.ChainedHeader;

            if (chainedHeader == null)
            {
                this.logger.LogTrace("(-)[REORG]");
                return;
            }

            this.logger.LogTrace("Block hash is '{0}'.", chainedHeader.HashBlock);

            bool isIBD = this.initialBlockDownloadState.IsInitialBlockDownload();

            // Ensure the block is written to disk before relaying.
            this.AddBlockToQueue(blockPair, isIBD);

            if (isIBD)
            {
                this.logger.LogTrace("(-)[IBD]");
                return;
            }

            if (this.storeSettings.PruningEnabled)
            {
                this.logger.LogTrace("(-)[PRUNE]");
                return;
            }

            this.logger.LogTrace("Block header '{0}' added to the announce queue.", chainedHeader);
            this.blocksToAnnounce.Enqueue(chainedHeader);
        }
Esempio n. 4
0
        public void OnNextCore_WhenTransactionsMissingInLongestChain_ReturnsThemToTheMempool()
        {
            var mempoolValidatorMock = new Mock <IMempoolValidator>();
            var mempoolMock          = new Mock <ITxMempool>();
            var loggerFactoryMock    = new Mock <ILoggerFactory>();

            loggerFactoryMock.Setup(i => i.CreateLogger(It.IsAny <string>())).Returns(new Mock <ILogger>().Object);

            Signals.Signals signals = new Signals.Signals(loggerFactoryMock.Object, null);
            var             subject = new BlocksDisconnectedSignaled(mempoolMock.Object, mempoolValidatorMock.Object, new MempoolSchedulerLock(), loggerFactoryMock.Object, signals);

            subject.Initialize();

            var block = new Block();
            var genesisChainedHeaderBlock = new ChainedHeaderBlock(block, ChainedHeadersHelper.CreateGenesisChainedHeader());
            var transaction1 = new Transaction();
            var transaction2 = new Transaction();

            block.Transactions = new List <Transaction> {
                transaction1, transaction2
            };

            signals.Publish(new BlockDisconnected(genesisChainedHeaderBlock));

            mempoolValidatorMock.Verify(x => x.AcceptToMemoryPool(It.IsAny <MempoolValidationState>(), transaction1));
            mempoolValidatorMock.Verify(x => x.AcceptToMemoryPool(It.IsAny <MempoolValidationState>(), transaction2));
        }
Esempio n. 5
0
        /// <summary>
        /// When a block is received it is passed to the monitor.
        /// </summary>
        /// <param name="chainedHeaderBlock">The new block.</param>
        public void OnBlockReceived(ChainedHeaderBlock chainedHeaderBlock)
        {
            this.walletSyncManager.ProcessBlock(chainedHeaderBlock.Block);

            // Cancel previous sending to avoid first sending new tip and then sending older tip.
            if ((this.pushBlockTipTask != null) && !this.pushBlockTipTask.IsCompleted)
            {
                this.cancellationSource.Cancel();
                this.pushBlockTipTask.GetAwaiter().GetResult();

                this.pushBlockTipTask   = null;
                this.cancellationSource = null;
            }

            var blockTipModel = new BlockTipModel(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock.ChainedHeader.Height, (int)this.depositExtractor.MinimumDepositConfirmations);

            // There is no reason to wait for the message to be sent.
            // Awaiting REST API call will only slow this callback.
            // Callbacks never supposed to do any IO calls or web requests.
            // Instead we start sending the message and if next block was connected faster than the message was sent we
            // are canceling it and sending the next tip.
            // Receiver of this message doesn't care if we are not providing tips for every block we connect,
            // it just requires to know about out latest state.
            this.cancellationSource = new CancellationTokenSource();
            this.pushBlockTipTask   = Task.Run(async() => await this.federationGatewayClient.PushCurrentBlockTipAsync(blockTipModel, this.cancellationSource.Token).ConfigureAwait(false));

            IReadOnlyList <IWithdrawal> withdrawals = this.withdrawalExtractor.ExtractWithdrawalsFromBlock(
                chainedHeaderBlock.Block,
                chainedHeaderBlock.ChainedHeader.Height);

            this.withdrawalReceiver.ReceiveWithdrawals(withdrawals);
        }
Esempio n. 6
0
        /// <inheritdoc />
        public void AddToPending(ChainedHeaderBlock chainedHeaderBlock)
        {
            // Throw any error encountered by the asynchronous loop.
            if (this.saveAsyncLoopException != null)
            {
                throw this.saveAsyncLoopException;
            }

            lock (this.blocksCacheLock)
            {
                if (this.pendingBlocksCache.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock))
                {
                    this.logger.LogDebug("Block '{0}' was added to pending.", chainedHeaderBlock.ChainedHeader);
                }
                else
                {
                    // If the chained header block already exists, we need to remove it and add to the back of the collection.
                    this.pendingBlocksCache.Remove(chainedHeaderBlock.ChainedHeader.HashBlock);
                    this.pendingBlocksCache.Add(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock);
                    this.logger.LogDebug("Block '{0}' was re-added to pending.", chainedHeaderBlock.ChainedHeader);
                }

                this.BlockStoreCacheTip = chainedHeaderBlock.ChainedHeader;
            }

            this.blocksQueue.Enqueue(chainedHeaderBlock);
            this.blocksQueueSizeBytes += chainedHeaderBlock.Block.BlockSize.Value;
        }
Esempio n. 7
0
        /// <summary>
        /// Cleans the batch in a way that all headers from the latest one are consecutive.
        /// Those that violate consecutiveness are removed.
        /// </summary>
        /// <returns>List of consecutive blocks.</returns>
        private List <ChainedHeaderBlock> GetBatchWithoutReorgedBlocks()
        {
            // Initialize current with highest block from the batch.
            ChainedHeaderBlock current = this.batch.Last();

            // List of consecutive blocks. It's a cleaned out version of batch that doesn't have blocks that were reorged.
            var batchCleared = new List <ChainedHeaderBlock>(this.batch.Count)
            {
                current
            };

            // Select only those blocks that were not reorged away.
            for (int i = this.batch.Count - 2; i >= 0; i--)
            {
                if (this.batch[i].ChainedHeader.HashBlock != current.ChainedHeader.Previous.HashBlock)
                {
                    this.logger.LogDebug("Block '{0}' removed from the batch because it was reorged.", this.batch[i].ChainedHeader);
                    continue;
                }

                batchCleared.Add(this.batch[i]);
                current = this.batch[i];
            }

            batchCleared.Reverse();

            return(batchCleared);
        }
Esempio n. 8
0
        private void OnBlockDisconnected(BlockDisconnected blockDisconnected)
        {
            ChainedHeaderBlock chBlock = blockDisconnected.DisconnectedBlock;

            lock (this.locker)
            {
                foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollExecutedBlockData?.Hash == chBlock.ChainedHeader.HashBlock).ToList())
                {
                    this.logger.LogDebug("Reverting poll execution '{0}'.", poll);
                    this.pollResultExecutor.RevertChange(poll.VotingData);

                    poll.PollExecutedBlockData = null;
                    this.pollsRepository.UpdatePoll(poll);
                }
            }

            byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

            if (rawVotingData == null)
            {
                this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                return;
            }

            List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

            votingDataList.Reverse();

            lock (this.locker)
            {
                foreach (VotingData votingData in votingDataList)
                {
                    // Poll that was finished in the block being disconnected.
                    Poll targetPoll = this.polls.Single(x => x.VotingData == votingData);

                    this.logger.LogDebug("Reverting poll voting in favor: '{0}'.", targetPoll);

                    if (targetPoll.PollVotedInFavorBlockData == new HashHeightPair(chBlock.ChainedHeader))
                    {
                        targetPoll.PollVotedInFavorBlockData = null;

                        this.pollsRepository.UpdatePoll(targetPoll);
                    }

                    // Pub key of a fed member that created voting data.
                    string fedMemberKeyHex = this.slotsManager.GetPubKeyForTimestamp(chBlock.Block.Header.Time).ToHex();

                    targetPoll.PubKeysHexVotedInFavor.Remove(fedMemberKeyHex);

                    if (targetPoll.PubKeysHexVotedInFavor.Count == 0)
                    {
                        this.polls.Remove(targetPoll);
                        this.pollsRepository.RemovePolls(targetPoll.Id);

                        this.logger.LogDebug("Poll with Id {0} was removed.", targetPoll.Id);
                    }
                }
            }
        }
Esempio n. 9
0
        public BlockDisconnected(ChainedHeaderBlock disconnectedBlock)
        {
            this.DisconnectedBlock = disconnectedBlock;

            this.Hash = this.DisconnectedBlock.ChainedHeader.HashBlock;

            this.Height = this.DisconnectedBlock.ChainedHeader.Height;
        }
        public void BlockObserverShouldSendExtractedWithdrawalsToWithdrawalReceiver()
        {
            ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock;

            this.blockObserver.OnBlockReceived(chainedHeaderBlock);

            this.withdrawalReceiver.ReceivedWithAnyArgs(1).ReceiveWithdrawals(Arg.Is(this.extractedWithdrawals));
        }
Esempio n. 11
0
        public BlockConnected(ChainedHeaderBlock connectedBlock)
        {
            this.ConnectedBlock = connectedBlock;

            this.Hash = this.ConnectedBlock.ChainedHeader.HashBlock;

            this.Height = this.ConnectedBlock.ChainedHeader.Height;
        }
Esempio n. 12
0
        public void BlockObserver_Should_Send_Block_Tip()
        {
            ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock;

            this.blockObserver.OnNext(chainedHeaderBlock);

            this.blockTipSender.ReceivedWithAnyArgs(1).SendBlockTipAsync(null);
        }
Esempio n. 13
0
        public void BlockObserver_Should_Extract_Withdrawals()
        {
            ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock;

            this.blockObserver.OnNext(chainedHeaderBlock);

            this.withdrawalExtractor.ReceivedWithAnyArgs(1).ExtractWithdrawalsFromBlock(null, 0);
        }
        /// <summary>
        /// Adds a block to the saving queue.
        /// </summary>
        /// <param name="chainedHeaderBlock">The block and its chained header pair to be added to pending storage.</param>
        public void AddToPending(ChainedHeaderBlock chainedHeaderBlock)
        {
            this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock.ChainedHeader);

            this.blocksQueue.Enqueue(chainedHeaderBlock);

            this.logger.LogTrace("(-)");
        }
Esempio n. 15
0
        public void BlockObserver_Should_Send_Extracted_Withdrawals_To_WithdrawalReceiver()
        {
            ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock;

            this.blockObserver.OnNext(chainedHeaderBlock);

            this.withdrawalReceiver.ReceivedWithAnyArgs(1).ReceiveWithdrawals(Arg.Is(this.extractedWithdrawals));
        }
        /// <inheritdoc />
        public void AddToPending(ChainedHeaderBlock chainedHeaderBlock)
        {
            lock (this.blocksCacheLock)
            {
                this.pendingBlocksCache.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock);
            }

            this.blocksQueue.Enqueue(chainedHeaderBlock);
        }
Esempio n. 17
0
        private Task ProcessBlockInternal(ChainedHeaderBlock chainedHeaderBlock, CancellationToken cancellationToken)
        {
            lock (this.txLock)
            {
                // No work to do
                if (this.ConsolidationTransactions == null)
                {
                    return(Task.CompletedTask);
                }

                // If a consolidation transaction comes through, set it to SeenInBlock
                foreach (Transaction transaction in chainedHeaderBlock.Block.Transactions)
                {
                    if (this.IsConsolidatingTransaction(transaction))
                    {
                        ConsolidationTransaction inMemoryTransaction =
                            this.GetInMemoryConsolidationTransaction(transaction);

                        if (inMemoryTransaction != null)
                        {
                            this.logger.Debug("Saw condensing transaction {0}, updating status to SeenInBlock",
                                              transaction.GetHash());
                            inMemoryTransaction.Status = ConsolidationTransactionStatus.SeenInBlock;
                        }
                    }
                }

                // Need to check all the transactions that are partial are still valid in case of a reorg.
                List <ConsolidationTransaction> partials = this.ConsolidationTransactions
                                                           .Where(x => x.Status == ConsolidationTransactionStatus.Partial ||
                                                                  x.Status == ConsolidationTransactionStatus.FullySigned)
                                                           .Take(5) // We don't actually need to validate all of them - just the next potential ones.
                                                           .ToList();

                foreach (ConsolidationTransaction cTransaction in partials)
                {
                    if (!this.walletManager.ValidateConsolidatingTransaction(cTransaction.PartialTransaction))
                    {
                        // If we find an invalid one, everything will need redoing!
                        this.logger.Debug(
                            "Consolidation transaction {0} failed validation, resetting InputConsolidator",
                            cTransaction.PartialTransaction.GetHash());
                        this.ConsolidationTransactions = null;
                        return(Task.CompletedTask);
                    }
                }

                // If all of our consolidation inputs are SeenInBlock, we can move on! Yay
                if (this.ConsolidationTransactions.All(x => x.Status == ConsolidationTransactionStatus.SeenInBlock))
                {
                    this.ConsolidationTransactions = null;
                }
            }

            return(Task.CompletedTask);
        }
        // This test requires retries because we are asserting result of a background thread that calls API.
        // This will work in real life but in tests it produces a race condition and therefore requires retries.
        public async Task BlockObserverShouldSendBlockTipAsync()
        {
            ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock;

            this.blockObserver.OnBlockReceived(chainedHeaderBlock);

            await Task.Delay(5000).ConfigureAwait(false);

            await this.federationGatewayClient.ReceivedWithAnyArgs(1).PushCurrentBlockTipAsync(null);
        }
Esempio n. 19
0
        public void SignalBlockDisconnectedToBlockSignaler()
        {
            Block block = KnownNetworks.StratisMain.CreateBlock();
            ChainedHeader header = ChainedHeadersHelper.CreateGenesisChainedHeader();
            var chainedHeaderBlock = new ChainedHeaderBlock(block, header);

            this.signals.SignalBlockDisconnected(chainedHeaderBlock);

            this.blockDisconnectedSignaler.Verify(b => b.Broadcast(chainedHeaderBlock), Times.Exactly(1));
        }
        private void OnBlockConnected(ChainedHeaderBlock chainedHeaderBlock)
        {
            ChainedHeader blockHeader = chainedHeaderBlock.ChainedHeader;

            Task task = this.RemoveForBlock(chainedHeaderBlock.Block, blockHeader?.Height ?? -1);

            // wait for the mempool code to complete
            // until the signaler becomes async
            task.GetAwaiter().GetResult();
        }
Esempio n. 21
0
        /// <summary>
        /// Provides block data for the given block hashes.
        /// </summary>
        /// <remarks>
        /// First we check if the block exists in chained header tree, then it check the block store and if it wasn't found there the block will be scheduled for download.
        /// Given callback is called when the block is obtained. If obtaining the block fails the callback will be called with <c>null</c>.
        /// </remarks>
        /// <param name="blockHashes">The block hashes to download.</param>
        /// <param name="onBlockDownloadedCallback">The callback that will be called for each downloaded block.</param>
        public async Task GetOrDownloadBlocksAsync(List <uint256> blockHashes, OnBlockDownloadedCallback onBlockDownloadedCallback)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(blockHashes), nameof(blockHashes.Count), blockHashes.Count);

            var blocksToDownload = new List <ChainedHeader>();

            for (int i = 0; i < blockHashes.Count; i++)
            {
                uint256            blockHash          = blockHashes[i];
                ChainedHeaderBlock chainedHeaderBlock = null;

                lock (this.peerLock)
                {
                    chainedHeaderBlock = this.chainedHeaderTree.GetChainedHeaderBlock(blockHash);
                }

                if (chainedHeaderBlock == null)
                {
                    this.logger.LogTrace("Block hash '{0}' is not part of the tree.", blockHash);
                    onBlockDownloadedCallback(null);
                    continue;
                }

                if (chainedHeaderBlock.Block != null)
                {
                    this.logger.LogTrace("Block pair '{0}' was found in memory.", chainedHeaderBlock);
                    onBlockDownloadedCallback(chainedHeaderBlock);
                    continue;
                }

                if (this.blockStore != null)
                {
                    Block block = await this.blockStore.GetBlockAsync(blockHash).ConfigureAwait(false);

                    if (block != null)
                    {
                        var newBlockPair = new ChainedHeaderBlock(block, chainedHeaderBlock.ChainedHeader);
                        this.logger.LogTrace("Chained header block '{0}' was found in store.", newBlockPair);
                        onBlockDownloadedCallback(newBlockPair);
                        continue;
                    }
                }

                blocksToDownload.Add(chainedHeaderBlock.ChainedHeader);
                this.logger.LogTrace("Block hash '{0}' is queued for download.", blockHash);
            }

            if (blocksToDownload.Count != 0)
            {
                this.logger.LogTrace("Asking block puller for {0} blocks.", blocksToDownload.Count);
                this.DownloadBlocks(blocksToDownload.ToArray(), this.ProcessDownloadedBlock);
            }

            this.logger.LogTrace("(-)");
        }
Esempio n. 22
0
        public Task <MerkleBlock> GetTxOutProofAsync(string[] txids, string blockhash = "")
        {
            List <uint256> transactionIds = txids.Select(txString => uint256.Parse(txString)).ToList();

            ChainedHeaderBlock block = null;

            if (!string.IsNullOrEmpty(blockhash))
            {
                // We presume that all the transactions are supposed to be contained in the same specified block, so we only retrieve it once.
                uint256 hashBlock = uint256.Parse(blockhash);

                block = this.consensusManager?.GetBlockData(hashBlock);
            }
            else
            {
                if (!(this.storeSettings?.TxIndex ?? false))
                {
                    throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Cannot find block containing specified transactions if hash is not specified and -txindex is disabled");
                }

                // Loop through txids and try to find which block they're in. Exit loop once a block is found, as they are supposed to all be in the same block.
                foreach (uint256 transactionId in transactionIds)
                {
                    ChainedHeader chainedHeader = this.GetTransactionBlock(transactionId);

                    if (chainedHeader?.BlockDataAvailability != null && chainedHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable)
                    {
                        block = this.consensusManager?.GetBlockData(chainedHeader.HashBlock);
                        break;
                    }
                }
            }

            if (block == null)
            {
                throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Block not found");
            }

            // Need to be able to lookup the transactions within the block efficiently.
            HashSet <uint256> transactionMap = block.Block.Transactions.Select(t => t.GetHash()).ToHashSet();

            // Loop through txids and verify that every transaction is in the block.
            foreach (uint256 transactionId in transactionIds)
            {
                if (!transactionMap.Contains(transactionId))
                {
                    throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Not all transactions found in specified or retrieved block");
                }
            }

            var result = new MerkleBlock(block.Block, transactionIds.ToArray());

            return(Task.FromResult(result));
        }
Esempio n. 23
0
        private ChainedHeaderBlock[] GetBlocks(int count, Func <int, ChainedHeaderBlock> block)
        {
            ChainedHeader previous = null;

            return(Enumerable.Range(0, count).Select(i =>
            {
                ChainedHeaderBlock chainedHeaderBlock = block(i);
                chainedHeaderBlock.ChainedHeader.SetPrivatePropertyValue("Previous", previous);
                previous = chainedHeaderBlock.ChainedHeader;
                return chainedHeaderBlock;
            }).ToArray());
        }
Esempio n. 24
0
        /// <inheritdoc />
        public void StartPartialValidation(ChainedHeaderBlock chainedHeaderBlock, OnPartialValidationCompletedAsyncCallback onPartialValidationCompletedAsyncCallback)
        {
            this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock);

            this.asyncQueue.Enqueue(new PartialValidationItem()
            {
                ChainedHeaderBlock = chainedHeaderBlock,
                PartialValidationCompletedAsyncCallback = onPartialValidationCompletedAsyncCallback
            });

            this.logger.LogTrace("(-)");
        }
Esempio n. 25
0
        public void OnNextCoreProcessesOnTheWalletSyncManager()
        {
            var           walletSyncManager  = new Mock <IWalletSyncManager>();
            var           observer           = new BlockObserver(walletSyncManager.Object);
            Block         block              = KnownNetworks.StratisMain.CreateBlock();
            ChainedHeader header             = ChainedHeadersHelper.CreateGenesisChainedHeader();
            var           chainedHeaderBlock = new ChainedHeaderBlock(block, header);

            observer.OnNext(chainedHeaderBlock);

            walletSyncManager.Verify(w => w.ProcessBlock(block), Times.Exactly(1));
        }
Esempio n. 26
0
        public void BlockDownloaded_NullBlock_CallsCallbacks()
        {
            TestContext builder = GetBuildTestContext(10, useCheckpoints: false, initializeWithChainTip: true);

            var additionalHeaders = builder.ExtendAChain(2, builder.InitialChainTip);
            var headerTree        = builder.ChainedHeaderToList(additionalHeaders, 2);
            var peer = builder.GetNetworkPeerWithConnection();

            var result = builder.TestConsensusManager.ConsensusManager.HeadersPresented(peer.Object, headerTree, true);

            Assert.NotNull(builder.blockPullerBlockDownloadCallback);

            builder.TestConsensusManager.SetExpectedBlockDataBytes(1000);

            var callback1Called            = false;
            var callback2Called            = false;
            var callback3Called            = false;
            ChainedHeaderBlock calledWith1 = null;
            ChainedHeaderBlock calledWith2 = null;
            ChainedHeaderBlock calledWith3 = null;
            var callback1 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback1Called = true; calledWith1 = d; });
            var callback2 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback2Called = true; calledWith2 = d; });
            var callback3 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback3Called = true; calledWith3 = d; });

            builder.TestConsensusManager.SetupCallbackByBlocksRequestedHash(additionalHeaders.HashBlock, callback1, callback2);
            builder.TestConsensusManager.SetupCallbackByBlocksRequestedHash(additionalHeaders.Previous.HashBlock, callback3);

            // call for both blocks.
            var block = new Block();

            block.ToBytes();
            builder.blockPullerBlockDownloadCallback(additionalHeaders.Previous.HashBlock, block, peer.Object.Connection.Id);
            builder.blockPullerBlockDownloadCallback(additionalHeaders.HashBlock, block, peer.Object.Connection.Id);

            Assert.False(builder.TestConsensusManager.CallbacksByBlocksRequestedHashContainsKeyForHash(additionalHeaders.HashBlock));
            Assert.False(builder.TestConsensusManager.CallbacksByBlocksRequestedHashContainsKeyForHash(additionalHeaders.Previous.HashBlock));

            builder.AssertExpectedBlockSizesEmpty();

            Assert.True(callback1Called);
            Assert.True(callback2Called);
            Assert.True(callback3Called);

            Assert.NotNull(calledWith1);
            Assert.Equal(additionalHeaders, calledWith1.ChainedHeader);

            Assert.NotNull(calledWith2);
            Assert.Equal(additionalHeaders, calledWith2.ChainedHeader);

            Assert.NotNull(calledWith3);
            Assert.Equal(additionalHeaders.Previous, calledWith3.ChainedHeader);
        }
Esempio n. 27
0
        /// <summary> Reconstructs voting and poll data from a given height.</summary>
        /// <param name="height">The height to start reconstructing from.</param>
        public void ReconstructVotingDataFromHeightLocked(int height)
        {
            try
            {
                this.isBusyReconstructing = true;

                var currentHeight = height;
                var progress      = $"Reconstructing voting poll data from height {currentHeight}.";
                this.logger.Info(progress);
                this.signals.Publish(new RecontructFederationProgressEvent()
                {
                    Progress = progress
                });

                do
                {
                    ChainedHeader chainedHeader = this.chainIndexer.GetHeader(currentHeight);
                    if (chainedHeader == null)
                    {
                        break;
                    }

                    Block block = this.blockRepository.GetBlock(chainedHeader.HashBlock);
                    if (block == null)
                    {
                        break;
                    }

                    var chainedHeaderBlock = new ChainedHeaderBlock(block, chainedHeader);

                    this.idleFederationMembersKicker.UpdateFederationMembersLastActiveTime(chainedHeaderBlock, false);

                    OnBlockConnected(new BlockConnected(chainedHeaderBlock));

                    currentHeight++;

                    if (currentHeight % 10000 == 0)
                    {
                        progress = $"Reconstructing voting data at height {currentHeight}";
                        this.logger.Info(progress);
                        this.signals.Publish(new RecontructFederationProgressEvent()
                        {
                            Progress = progress
                        });
                    }
                } while (true);
            }
            finally
            {
                this.isBusyReconstructing = false;
            }
        }
        /// <inheritdoc />
        public void AddToPending(ChainedHeaderBlock chainedHeaderBlock)
        {
            this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock.ChainedHeader);

            lock (this.getBlockLock)
            {
                this.pendingBlocks.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock);
            }

            this.blocksQueue.Enqueue(chainedHeaderBlock);

            this.logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        /// <remarks>When a block is signaled, we check if its header is a Proven Header, if not, we need to generate and store it.</remarks>
        protected override void AddBlockToQueue(ChainedHeaderBlock blockPair, bool isIBD)
        {
            var blockHeight = blockPair.ChainedHeader.Height;

            if (blockPair.ChainedHeader.Header is ProvenBlockHeader phHeader)
            {
                this.logger.LogDebug("Current header is already a Proven Header.");

                // Add to the store, to be sure we actually store it anyway.
                // It's ProvenBlockHeaderStore responsibility to prevent us to store it twice.
                this.provenBlockHeaderStore.AddToPendingBatch(phHeader,
                                                              new HashHeightPair(phHeader.GetHash(), blockHeight));
            }
            else
            {
                // Ensure we doesn't have already the ProvenHeader in the store.
                var provenHeader = this.provenBlockHeaderStore.GetAsync(blockPair.ChainedHeader.Height).GetAwaiter()
                                   .GetResult();

                // Proven Header not found? create it now.
                if (provenHeader == null)
                {
                    this.logger.LogDebug("Proven Header at height {0} NOT found.", blockHeight);

                    CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD);
                }
                else
                {
                    var signaledHeaderHash = blockPair.Block.Header.GetHash();

                    // If the Proven Header is the right one, then it's OK and we can return without doing anything.
                    var provenHeaderHash = provenHeader.GetHash();
                    if (provenHeaderHash == signaledHeaderHash)
                    {
                        this.logger.LogDebug("Proven Header {0} found.", signaledHeaderHash);
                    }
                    else
                    {
                        this.logger.LogDebug(
                            "Found a proven header with a different hash, recreating PH. Expected Hash: {0}, found Hash: {1}.",
                            signaledHeaderHash, provenHeaderHash);

                        // A reorg happened so we recreate a new Proven Header to replace the wrong one.
                        CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD);
                    }
                }
            }

            // At the end, if no exception happened, control is passed back to base AddBlockToQueue.
            base.AddBlockToQueue(blockPair, isIBD);
        }
Esempio n. 30
0
        /// <summary>
        /// Ensures that any blocks queued in <see cref="blocksQueue"/> gets added to <see cref="batch"/>
        /// so that it can be persisted on dispose.
        /// </summary>
        private void FlushAllCollections()
        {
            ChainedHeaderBlock chainedHeaderBlock = null;

            while (this.blocksQueue.TryDequeue(out chainedHeaderBlock))
            {
                this.batch.Add(chainedHeaderBlock);
            }

            if (this.batch.Count != 0)
            {
                this.SaveBatch();
            }
        }