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); }
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); }
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); }