Пример #1
0
        public async Task ExecuteBlocksAttachedToLongestChain_ValidateFailed()
        {
            var chain = await _blockchainService.GetChainAsync();

            var bestChainHeight = chain.BestChainHeight;
            var bestChainHash   = chain.BestChainHash;

            var previousHash   = chain.BestChainHash;
            var previousHeight = chain.BestChainHeight;

            BlockAttachOperationStatus status = BlockAttachOperationStatus.None;
            var blockList = new List <Block>();
//            Block lastBlock = null;
            int count = 0;

            while (!status.HasFlag(BlockAttachOperationStatus.LongestChainFound))
            {
                var transactions = new List <Transaction> {
                    _kernelTestHelper.GenerateTransaction()
                };
                var lastBlock = _kernelTestHelper.GenerateBlock(previousHeight, previousHash, transactions);

                await _blockchainService.AddBlockAsync(lastBlock);

                await _blockchainService.AddTransactionsAsync(transactions);

                status = await _blockchainService.AttachBlockToChainAsync(chain, lastBlock);

                count++;
                previousHash   = lastBlock.GetHash();
                previousHeight = lastBlock.Height;
                blockList.Add(lastBlock);
            }

            var attachResult =
                await _fullBlockchainExecutingService.ExecuteBlocksAttachedToLongestChain(chain, status);

            attachResult.ShouldBeNull();

            chain = await _blockchainService.GetChainAsync();

            var newBlockLink = await _chainManager.GetChainBlockLinkAsync(blockList.First().GetHash());

            newBlockLink.ExecutionStatus.ShouldBe(ChainBlockLinkExecutionStatus.ExecutionFailed);
            chain.BestChainHash.ShouldBe(bestChainHash);
            chain.BestChainHeight.ShouldBe(bestChainHeight);
            chain.LongestChainHash.ShouldBe(bestChainHash);
            chain.LongestChainHeight.ShouldBe(bestChainHeight);
            chain.Branches.ShouldNotContainKey(previousHash.ToStorageKey());
        }
        public async Task Attach_Block_To_Chain_FoundBestChain()
        {
            var chain = await _blockchainService.GetChainAsync();

            var previousHash   = chain.BestChainHash;
            var previousHeight = chain.BestChainHeight;

            BlockAttachOperationStatus status = BlockAttachOperationStatus.None;
//            Block lastBlock = null;
            int count = 0;

            while (!status.HasFlag(BlockAttachOperationStatus.LongestChainFound))
            {
                var transactions = new List <Transaction> {
                    _kernelTestHelper.GenerateTransaction()
                };
                var lastBlock = _kernelTestHelper.GenerateBlock(previousHeight, previousHash, transactions);

                await _blockchainService.AddBlockAsync(lastBlock);

                await _blockchainService.AddTransactionsAsync(transactions);

                status = await _blockchainService.AttachBlockToChainAsync(chain, lastBlock);

                count++;
                previousHash   = lastBlock.GetHash();
                previousHeight = lastBlock.Height;
            }

            var attachResult =
                await _fullBlockchainExecutingService.ExecuteBlocksAttachedToLongestChain(chain, status);

            attachResult.Count.ShouldBe(count);
            attachResult.Last().Height.ShouldBe(previousHeight);

            chain = await _blockchainService.GetChainAsync();

            chain.BestChainHash.ShouldBe(previousHash);
            chain.BestChainHeight.ShouldBe(previousHeight);
        }
Пример #3
0
        public async Task <List <ChainBlockLink> > ExecuteBlocksAttachedToLongestChain(Chain chain, BlockAttachOperationStatus status)
        {
            if (!status.HasFlag(BlockAttachOperationStatus.LongestChainFound))
            {
                Logger.LogTrace($"Try to attach to chain but the status is {status}.");
                return(null);
            }

            var successLinks = new List <ChainBlockLink>();
            var blockLinks   = await _chainManager.GetNotExecutedBlocks(chain.LongestChainHash);

            try
            {
                foreach (var blockLink in blockLinks)
                {
                    var linkedBlock = await _blockchainService.GetBlockByHashAsync(blockLink.BlockHash);

                    // Set the other blocks as bad block if found the first bad block
                    if (!await _blockValidationService.ValidateBlockBeforeExecuteAsync(linkedBlock))
                    {
                        await _chainManager.SetChainBlockLinkExecutionStatus(blockLink, ChainBlockLinkExecutionStatus.ExecutionFailed);

                        Logger.LogWarning($"Block validate fails before execution. block hash : {blockLink.BlockHash}");
                        break;
                    }

                    if (!await ExecuteBlock(blockLink, linkedBlock))
                    {
                        await _chainManager.SetChainBlockLinkExecutionStatus(blockLink, ChainBlockLinkExecutionStatus.ExecutionFailed);

                        Logger.LogWarning($"Block execution failed. block hash : {blockLink.BlockHash}");
                        break;
                    }

                    if (!await _blockValidationService.ValidateBlockAfterExecuteAsync(linkedBlock))
                    {
                        await _chainManager.SetChainBlockLinkExecutionStatus(blockLink, ChainBlockLinkExecutionStatus.ExecutionFailed);

                        Logger.LogWarning($"Block validate fails after execution. block hash : {blockLink.BlockHash}");
                        break;
                    }

                    await _chainManager.SetChainBlockLinkExecutionStatus(blockLink, ChainBlockLinkExecutionStatus.ExecutionSuccess);

                    successLinks.Add(blockLink);

                    Logger.LogInformation($"Executed block {blockLink.BlockHash} at height {blockLink.Height}.");

                    await LocalEventBus.PublishAsync(new BlockAcceptedEvent()
                    {
                        BlockHeader = linkedBlock.Header
                    });
                }
            }
            catch (ValidateNextTimeBlockValidationException ex)
            {
                Logger.LogWarning($"Block validate fails after execution. Exception message {ex.Message}");
            }

            if (successLinks.Count > 0)
            {
                var blockLink = successLinks.Last();
                await _blockchainService.SetBestChainAsync(chain, blockLink.Height, blockLink.BlockHash);

                await LocalEventBus.PublishAsync(new BestChainFoundEventData
                {
                    BlockHash      = chain.BestChainHash,
                    BlockHeight    = chain.BestChainHeight,
                    ExecutedBlocks = successLinks.Select(p => p.BlockHash).ToList()
                });
            }

            Logger.LogInformation($"Attach blocks to best chain, status: {status}, best chain hash: {chain.BestChainHash}, height: {chain.BestChainHeight}");

            return(blockLinks);
        }
Пример #4
0
        public async Task <BlockAttachOperationStatus> AttachBlockToChainAsync(Chain chain, ChainBlockLink chainBlockLink)
        {
            BlockAttachOperationStatus status = BlockAttachOperationStatus.None;

            bool isLinkedToLongestChain = chainBlockLink.PreviousBlockHash == chain.LongestChainHash &&
                                          chainBlockLink.Height == chain.LongestChainHeight + 1;

            Logger.LogDebug(
                $"Start attach block hash {chainBlockLink.BlockHash}, height {chainBlockLink.Height}");

            while (true)
            {
                var previousHash = chainBlockLink.PreviousBlockHash.ToStorageKey();
                var blockHash    = chainBlockLink.BlockHash.ToStorageKey();

                if (chain.Branches.ContainsKey(previousHash))
                {
                    chain.Branches[blockHash] = chainBlockLink.Height;
                    chain.Branches.Remove(previousHash);

                    if (isLinkedToLongestChain && chainBlockLink.Height > chain.LongestChainHeight ||
                        chainBlockLink.Height >= chain.LongestChainHeight + 8)
                    {
                        chain.LongestChainHeight = chainBlockLink.Height;
                        chain.LongestChainHash   = chainBlockLink.BlockHash;
                        status |= BlockAttachOperationStatus.LongestChainFound;
                    }

                    if (chainBlockLink.IsLinked)
                    {
                        throw new Exception("chain block link should not be linked");
                    }

                    chainBlockLink.IsLinked = true;

                    await SetChainBlockLinkAsync(chainBlockLink);

                    if (!chain.NotLinkedBlocks.ContainsKey(blockHash))
                    {
                        status |= BlockAttachOperationStatus.NewBlockLinked;
                        break;
                    }

                    chainBlockLink = await GetChainBlockLinkWithCacheAsync(chain.NotLinkedBlocks[blockHash]);

                    chain.NotLinkedBlocks.Remove(blockHash);

                    status |= BlockAttachOperationStatus.NewBlocksLinked;
                }
                else
                {
                    //check database to ensure whether it can be a branch
                    var previousChainBlockLink = await this.GetChainBlockLinkAsync(chainBlockLink.PreviousBlockHash);

                    if (previousChainBlockLink != null && previousChainBlockLink.IsLinked)
                    {
                        chain.Branches[previousChainBlockLink.BlockHash.ToStorageKey()] = previousChainBlockLink.Height;
                        continue;
                    }

                    chain.NotLinkedBlocks[previousHash] = blockHash;

                    if (status != BlockAttachOperationStatus.None)
                    {
                        throw new Exception("invalid status");
                    }

                    status = BlockAttachOperationStatus.NewBlockNotLinked;
                    await SetChainBlockLinkAsync(chainBlockLink);

                    break;
                }
            }

            await _chains.SetAsync(chain.Id.ToStorageKey(), chain);

            Logger.LogInformation($"Attach {chainBlockLink.BlockHash} to longest chain, status: {status}, " +
                                  $"longest chain height: {chain.LongestChainHeight}, longest chain hash: {chain.LongestChainHash}");

            return(status);
        }
        public async Task <List <ChainBlockLink> > ExecuteBlocksAttachedToLongestChain(Chain chain,
                                                                                       BlockAttachOperationStatus status)
        {
            if (!status.HasFlag(BlockAttachOperationStatus.LongestChainFound))
            {
                Logger.LogTrace($"Try to attach to chain but the status is {status}.");
                return(null);
            }

            var successLinks  = new List <ChainBlockLink>();
            var successBlocks = new List <Block>();
            var blockLinks    = await _chainManager.GetNotExecutedBlocks(chain.LongestChainHash);

            try
            {
                foreach (var blockLink in blockLinks)
                {
                    var linkedBlock = await _blockchainService.GetBlockByHashAsync(blockLink.BlockHash);

                    var processResult = await TryProcessBlockAsync(linkedBlock);

                    if (!processResult)
                    {
                        await _chainManager.SetChainBlockLinkExecutionStatusAsync(blockLink,
                                                                                  ChainBlockLinkExecutionStatus.ExecutionFailed);

                        await _chainManager.RemoveLongestBranchAsync(chain);

                        return(null);
                    }

                    successLinks.Add(blockLink);
                    successBlocks.Add(linkedBlock);
                    Logger.LogInformation(
                        $"Executed block {blockLink.BlockHash} at height {blockLink.Height}, with {linkedBlock.Body.TransactionsCount} txns.");

                    await LocalEventBus.PublishAsync(new BlockAcceptedEvent { Block = linkedBlock });
                }
            }
            catch (BlockValidationException ex)
            {
                if (!(ex.InnerException is ValidateNextTimeBlockValidationException))
                {
                    await _chainManager.RemoveLongestBranchAsync(chain);

                    throw;
                }

                Logger.LogWarning(
                    $"Block validation failed: {ex.Message}. Inner exception {ex.InnerException.Message}");
            }
            catch (Exception ex)
            {
                await _chainManager.RemoveLongestBranchAsync(chain);

                Logger.LogWarning($"Block validate or execute fails. Exception message {ex.Message}");
                throw;
            }

            if (successLinks.Count == 0 || successLinks.Last().Height < chain.BestChainHeight)
            {
                Logger.LogWarning("No block execution succeed or no block is higher than best chain.");
                await _chainManager.RemoveLongestBranchAsync(chain);

                return(null);
            }

            await SetBestChainAsync(successLinks, chain);

            await _chainManager.SetChainBlockLinkExecutionStatusesAsync(successLinks,
                                                                        ChainBlockLinkExecutionStatus.ExecutionSuccess);

            await PublishBestChainFoundEventAsync(chain, successBlocks);

            Logger.LogInformation(
                $"Attach blocks to best chain, status: {status}, best chain hash: {chain.BestChainHash}, height: {chain.BestChainHeight}");

            return(blockLinks);
        }