public async Task HandleEventAsync(ConsensusRequestMiningEventData eventData) { try { _taskQueueManager.Enqueue(async() => { if (eventData.BlockTime > new Timestamp { Seconds = 3600 } && eventData.BlockTime + eventData.BlockExecutionTime < TimestampHelper.GetUtcNow()) { Logger.LogTrace( $"Will cancel mining due to timeout: Actual mining time: {eventData.BlockTime}, " + $"execution limit: {eventData.BlockExecutionTime.Milliseconds()} ms."); } var block = await _minerService.MineAsync(eventData.PreviousBlockHash, eventData.PreviousBlockHeight, eventData.BlockTime, eventData.BlockExecutionTime); await _blockchainService.AddBlockAsync(block); var chain = await _blockchainService.GetChainAsync(); await LocalEventBus.PublishAsync(new BlockMinedEventData() { BlockHeader = block.Header, HasFork = block.Height <= chain.BestChainHeight }); // Self mined block do not need do verify _taskQueueManager.Enqueue(async() => await _blockAttachService.AttachBlockAsync(block), KernelConstants.UpdateChainQueueName); }, KernelConstants.ConsensusRequestMiningQueueName); } catch (Exception e) { Logger.LogError(e.ToString()); throw; } }
private bool ValidateBlockMiningTime(Timestamp blockTime, Timestamp miningDueTime, Duration blockExecutionDuration) { if (IsGenesisBlockMining(blockTime)) { return(true); } if (miningDueTime < blockTime + blockExecutionDuration) { Logger.LogWarning( $"Mining canceled because mining time slot expired. MiningDueTime: {miningDueTime}, BlockTime: {blockTime}, Duration: {blockExecutionDuration}"); return(false); } if (blockTime + blockExecutionDuration < TimestampHelper.GetUtcNow()) { Logger.LogTrace($"Will cancel mining due to timeout: Actual mining time: {blockTime}, " + $"execution limit: {blockExecutionDuration.Milliseconds()} ms."); return(false); } return(true); }
public async Task HandleEvent_Test() { BlockMinedEventData blockMinedEventData = null; _localEventBus.Subscribe <BlockMinedEventData>(d => { blockMinedEventData = d; return(Task.CompletedTask); }); var chain = await _blockchainService.GetChainAsync(); var bestChainHash = chain.BestChainHash; var bestChainHeight = chain.BestChainHeight; _testContext.MockConsensusService.Verify( s => s.TriggerConsensusAsync(It.IsAny <ChainContext>()), Times.Exactly(10)); { var eventData = new ConsensusRequestMiningEventData(HashHelper.ComputeFrom("NotBestChain"), bestChainHeight, TimestampHelper.GetUtcNow(), TimestampHelper.DurationFromMilliseconds(500), TimestampHelper.GetUtcNow().AddMilliseconds(499)); await HandleConsensusRequestMiningEventAsync(eventData); blockMinedEventData.ShouldBeNull(); chain = await _blockchainService.GetChainAsync(); chain.BestChainHeight.ShouldBe(bestChainHeight); chain.BestChainHash.ShouldBe(bestChainHash); _testContext.MockConsensusService.Verify( s => s.TriggerConsensusAsync(It.IsAny <ChainContext>()), Times.Exactly(10)); } { var eventData = new ConsensusRequestMiningEventData(bestChainHash, bestChainHeight, TimestampHelper.GetUtcNow(), TimestampHelper.DurationFromMilliseconds(500), TimestampHelper.GetUtcNow().AddMilliseconds(499)); await HandleConsensusRequestMiningEventAsync(eventData); blockMinedEventData.ShouldBeNull(); chain = await _blockchainService.GetChainAsync(); chain.BestChainHeight.ShouldBe(bestChainHeight); chain.BestChainHash.ShouldBe(bestChainHash); _testContext.MockConsensusService.Verify( s => s.TriggerConsensusAsync(It.IsAny <ChainContext>()), Times.Exactly(11)); } { var eventData = new ConsensusRequestMiningEventData(bestChainHash, bestChainHeight, TimestampHelper.GetUtcNow(), TimestampHelper.DurationFromMilliseconds(500), TimestampHelper.GetUtcNow().AddSeconds(30)); await HandleConsensusRequestMiningEventAsync(eventData); blockMinedEventData.ShouldNotBeNull(); blockMinedEventData.BlockHeader.Height.ShouldBe(bestChainHeight + 1); blockMinedEventData.BlockHeader.PreviousBlockHash.ShouldBe(bestChainHash); chain = await _blockchainService.GetChainAsync(); chain.Branches.ShouldContainKey(blockMinedEventData.BlockHeader.GetHash().ToStorageKey()); (await _blockchainService.HasBlockAsync(blockMinedEventData.BlockHeader.GetHash())).ShouldBeTrue(); _testContext.MockConsensusService.Verify( s => s.TriggerConsensusAsync(It.IsAny <ChainContext>()), Times.Exactly(11)); } }
public async Task MinAsync_Success_Test() { var chain = await _chainService.GetChainAsync(); var hash = chain.BestChainHash; var height = chain.BestChainHeight; var blockHeader = await _minerService.CreateTemplateCacheAsync(hash, height, TimestampHelper.GetUtcNow(), TimestampHelper.DurationFromMinutes(1)); var byteString = blockHeader.ToByteString(); var bytes = byteString.ToByteArray(); //Send Bytes to Client #region Client Side //Client side, you can search nonce and replace it var nonce = BitConverter.GetBytes(long.MaxValue - 1); var start = bytes.Find(nonce); start.ShouldBeGreaterThan(0); for (int i = 0; i < nonce.Length; i++) { bytes[start + i] = 9; //change nonce } bytes.Find(nonce).ShouldBe(-1); var newHeader = BlockHeader.Parser.ParseFrom(ByteString.CopyFrom(bytes)); //Test mining method newHeader.GetHash().ShouldBe(Hash.FromRawBytes(newHeader.ToByteArray())); newHeader.GetHash().ShouldBe(Hash.FromRawBytes(bytes)); //Start mining Random r = new Random(); while (Hash.FromRawBytes(bytes).Value[0] != 0) { //find first hash byte is 0 for (int i = 0; i < nonce.Length; i++) { bytes[start + i] = (byte)r.Next(); //change nonce, very slow, just for demo } } #endregion //Send bytes to Server newHeader = BlockHeader.Parser.ParseFrom(ByteString.CopyFrom(bytes)); var newHeaderHash = newHeader.GetHash(); newHeaderHash.Value[0].ShouldBe((byte)0); // first byte should be zero var block = await _minerService.ChangeTemplateCacheBlockHeaderAndClearCacheAsync(newHeader); block.GetHash().ShouldBe(newHeader.GetHash()); // check new block's header block.Header.Signature.ShouldBeEmpty(); // check signature }
public Task HandleEventAsync(ConsensusRequestMiningEventData eventData) { try { _taskQueueManager.Enqueue(async() => { var chain = await _blockchainService.GetChainAsync(); if (eventData.PreviousBlockHash != chain.BestChainHash) { Logger.LogWarning("Mining canceled because best chain already updated."); return; } if (!ValidateBlockMiningTime(eventData.BlockTime, eventData.MiningDueTime, eventData.BlockExecutionTime)) { await TriggerConsensusEventAsync(chain.BestChainHash, chain.BestChainHeight); return; } var blockExecutionDuration = CalculateBlockMiningDuration(eventData.BlockTime, eventData.BlockExecutionTime); Block block; try { block = await _minerService.MineAsync(eventData.PreviousBlockHash, eventData.PreviousBlockHeight, eventData.BlockTime, blockExecutionDuration); } catch (Exception) { await TriggerConsensusEventAsync(chain.BestChainHash, chain.BestChainHeight); throw; } if (TimestampHelper.GetUtcNow() <= eventData.MiningDueTime - blockExecutionDuration) { await _blockchainService.AddBlockAsync(block); Logger.LogTrace("Before enqueue attach job."); _taskQueueManager.Enqueue(async() => await _blockAttachService.AttachBlockAsync(block), KernelConstants.UpdateChainQueueName); Logger.LogTrace("Before publish block."); await LocalEventBus.PublishAsync(new BlockMinedEventData { BlockHeader = block.Header, // HasFork = block.Height <= chain.BestChainHeight }); } else { Logger.LogWarning( $"Discard block {block.Height} and trigger once again because mining time slot expired. " + $"MiningDueTime : {eventData.MiningDueTime}, " + $"block execution duration limit : {blockExecutionDuration}"); await TriggerConsensusEventAsync(chain.BestChainHash, chain.BestChainHeight); } }, KernelConstants.ConsensusRequestMiningQueueName); return(Task.CompletedTask); } catch (Exception e) { Logger.LogError(e.ToString()); throw; } }
public async Task HandleEventAsync_Test() { var chain = await _chainService.GetChainAsync(); var hash = chain.BestChainHash; var height = chain.BestChainHeight; var eventData = new ConsensusRequestMiningEventData(hash, height, TimestampHelper.GetUtcNow(), TimestampHelper.DurationFromSeconds(60), TimestampHelper.GetUtcNow().AddDays(1)); await _miningEventHandler.HandleEventAsync(eventData); }