public async void should_verify_transactions_when_block_is_tip()
        {
            //arrange
            var block = BuildBlock();

            GivenVerificationPasses(block);
            GivenBlockIsNextTip(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty);

            A.CallTo(() => _blockVerifier.VerifyTransactions(block))
            .Returns(false);

            //act
            var result = await _subject.RecieveBlock(block);

            //assert
            A.CallTo(() => _blockVerifier.VerifyTransactions(block))
            .MustHaveHappened();

            A.CallTo(() => _blockRepository.AddBlock(block))
            .MustNotHaveHappened();

            A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions))
            .MustNotHaveHappened();

            result.Should().Be(PeerDataResult.Demerit);
        }
        private async Task <PeerDataResult> RecieveBlockUnprotected(Block block)
        {
            var isTip = false;

            _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)}");

            if (await _blockRepository.HavePrimaryBlock(block.Header.BlockId))
            {
                _logger.LogInformation("already have block");
                return(PeerDataResult.Ignore);
            }

            if (!await _blockVerifier.Verify(block))
            {
                _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}");
                return(PeerDataResult.Demerit);
            }

            if (!await _blockVerifier.VerifyBlockRules(block, true))
            {
                _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}");
                return(PeerDataResult.Demerit);
            }

            var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock);

            var bestHeader = await _blockRepository.GetBestBlockHeader();

            var isEmpty = await _blockRepository.IsEmpty();

            bool mainChain   = false;
            bool rebaseChain = false;

            isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey));
            _logger.LogInformation($"Is Tip {isTip}");

            if (prevHeader != null)
            {
                _logger.LogInformation("Do Have previous block");

                if (block.Header.Timestamp < prevHeader.Timestamp)
                {
                    _logger.LogInformation("Timestamps dont match");
                    return(PeerDataResult.Ignore);
                }

                if (block.Header.Height != (prevHeader.Height + 1))
                {
                    _logger.LogInformation($"Height mismatch prev: {prevHeader.Height}, this: {block.Header.Height}");
                    return(PeerDataResult.Ignore);
                }

                if (!isTip)
                {
                    var prevMain = await _blockRepository.GetPrimaryHeader(block.Header.Height - 1);

                    var isPrevOnMainChain = prevMain?.BlockId.SequenceEqual(block.Header.PreviousBlock) ?? false;
                    var mainExisiting     = await _blockRepository.GetPrimaryHeader(block.Header.Height);

                    mainChain = ((mainExisiting == null) && (isPrevOnMainChain));
                }
                else
                {
                    mainChain = true;
                }

                rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain);

                _logger.LogInformation($"Processing block, have prev, main chain: {mainChain}, rebase: {rebaseChain}");

                var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp);

                if ((mainChain) && (block.Header.Difficulty < expectedDifficulty))
                {
                    _logger.LogInformation("Difficulty mismatch");
                    return(PeerDataResult.Ignore);
                }
            }
            else
            {
                _logger.LogInformation("Dont have prev block");
                if (isEmpty && !block.Header.PreviousBlock.SequenceEqual(Block.HeadKey))
                {
                    _logger.LogInformation("not first block but am empty");
                    return(PeerDataResult.Ignore);
                }
                mainChain = isEmpty;
                _logger.LogInformation($"Processing block, missing prev, main chain: {mainChain}, rebase: {rebaseChain}");
                _peerNetwork.RequestBlock(block.Header.PreviousBlock);
            }

            if (mainChain)
            {
                _logger.LogInformation("processing for main chain");
                if (!await _blockVerifier.VerifyTransactions(block))
                {
                    _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}");
                    return(PeerDataResult.Demerit);
                }

                await _blockRepository.AddBlock(block);

                _unconfirmedTransactionPool.Remove(block.Transactions);
            }
            else
            {
                if (!await _blockRepository.HaveSecondaryBlock(block.Header.BlockId))
                {
                    _logger.LogInformation($"Adding detached block");
                    await _blockRepository.AddSecondaryBlock(block);
                }
                if (rebaseChain)
                {
                    _logger.LogInformation($"Searching for divergent block");
                    var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId);

                    if (divergentHeader != null)
                    {
                        _logger.LogInformation($"Rebasing chain from {divergentHeader.Height}");
                        var replayBlocks = await _forkRebaser.RebaseChain(divergentHeader.BlockId, block.Header.BlockId);

                        _logger.LogInformation($"Replaying {replayBlocks.Count} blocks");
                        foreach (var replayBlock in replayBlocks)
                        {
                            var replayResult = await RecieveBlockUnprotected(replayBlock);

                            if (replayResult == PeerDataResult.Demerit)
                            {
                                break;
                            }
                        }
                        _logger.LogInformation($"Replay complete");
                    }
                    else
                    {
                        _logger.LogInformation($"Divergent block not found");
                        var firstForkHeader = await _forkRebaser.FindKnownForkbase(block.Header.BlockId);

                        if (firstForkHeader != null)
                        {
                            _peerNetwork.RequestBlock(firstForkHeader.PreviousBlock);
                        }
                    }
                }
            }

            if (isTip)
            {
                _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}");
                return(PeerDataResult.Relay);
            }
            else
            {
                _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}");
                return(PeerDataResult.Ignore);
            }
        }