/// <summary> /// Checks and computes stake. /// </summary> /// <param name="context">Context that contains variety of information regarding blocks validation and execution.</param> /// <exception cref="ConsensusErrors.PrevStakeNull">Thrown if previous stake is not found.</exception> /// <exception cref="ConsensusErrors.SetStakeEntropyBitFailed">Thrown if failed to set stake entropy bit.</exception> private void CheckAndComputeStake(RuleContext context) { ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; Block block = context.ValidationContext.BlockToValidate; var posRuleContext = context as PosRuleContext; if (posRuleContext.BlockStake == null) { posRuleContext.BlockStake = BlockStake.Load(context.ValidationContext.BlockToValidate); } BlockStake blockStake = posRuleContext.BlockStake; // Verify hash target and signature of coinstake tx. if (BlockStake.IsProofOfStake(block)) { ChainedHeader prevChainedHeader = chainedHeader.Previous; BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } // Only do proof of stake validation for blocks that are after the assumevalid block or after the last checkpoint. if (!context.SkipValidation) { this.stakeValidator.CheckProofOfStake(posRuleContext, prevChainedHeader, prevBlockStake, block.Transactions[1], chainedHeader.Header.Bits.ToCompact()); } else { this.Logger.LogTrace("POS validation skipped for block at height {0}.", chainedHeader.Height); } } // PoW is checked in CheckBlock(). if (BlockStake.IsProofOfWork(block)) { posRuleContext.HashProofOfStake = chainedHeader.Header.GetPoWHash(); } // Compute stake entropy bit for stake modifier. if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { this.Logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]"); ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value. blockStake.HashProof = posRuleContext.HashProofOfStake; int lastCheckpointHeight = this.Parent.Checkpoints.GetLastCheckpointHeight(); if (chainedHeader.Height > lastCheckpointHeight) { // Compute stake modifier. ChainedHeader prevChainedHeader = chainedHeader.Previous; BlockStake blockStakePrev = prevChainedHeader == null ? null : this.stakeChain.Get(prevChainedHeader.HashBlock); blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedHeader, blockStakePrev?.StakeModifierV2, blockStake.IsProofOfWork() ? chainedHeader.HashBlock : blockStake.PrevoutStake.Hash); } else if (chainedHeader.Height == lastCheckpointHeight) { // Copy checkpointed stake modifier. CheckpointInfo checkpoint = this.Parent.Checkpoints.GetCheckpoint(lastCheckpointHeight); blockStake.StakeModifierV2 = checkpoint.StakeModifierV2; this.Logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2); } else { this.Logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedHeader.Height, lastCheckpointHeight); } }
// Use this for initialization void Start() { checkpointinfo = CheckpointInfo.instance; }
public Task CheckpointAsync(CheckpointInfo checkpointInfo) { return(this.reliableLog.CheckpointAsync(checkpointInfo)); }
private void OnEnable() { currentCeckpoint = checkpoints[0]; nextCheckpointPos = checkpoints[1].position; StartCoroutine(MoveBetweenCheckpoints()); }
/// <summary> /// When a header is checkpointed and has a correct hash, chain that ends with such a header /// will be marked as <see cref="ValidationState.AssumedValid" /> and requested for download. /// </summary> /// <param name="chainedHeader">Checkpointed header.</param> /// <param name="latestNewHeader">The latest new header that was presented by the peer.</param> /// <param name="checkpoint">Information about the checkpoint at the height of the <paramref name="chainedHeader"/>.</param> private ConnectNewHeadersResult HandleCheckpointsHeader(ChainedHeader chainedHeader, ChainedHeader latestNewHeader, CheckpointInfo checkpoint) { this.logger.LogTrace("({0}:'{1}',{2}:'{3}',{4}.{5}:'{6}')", nameof(chainedHeader), chainedHeader, nameof(latestNewHeader), latestNewHeader, nameof(checkpoint), nameof(checkpoint.Hash), checkpoint.Hash); if (checkpoint.Hash != chainedHeader.HashBlock) { this.logger.LogDebug("Chained header '{0}' does not match checkpoint '{1}'.", chainedHeader, checkpoint.Hash); this.logger.LogTrace("(-)[INVALID_HEADER_NOT_MATCHING_CHECKPOINT]"); throw new InvalidHeaderException(); } ChainedHeader subchainTip = chainedHeader; if (chainedHeader.Height == this.checkpoints.GetLastCheckpointHeight()) { subchainTip = latestNewHeader; } ConnectNewHeadersResult connectNewHeadersResult = this.MarkBetterChainAsRequired(subchainTip); this.MarkTrustedChainAsAssumedValid(chainedHeader); this.logger.LogTrace("(-):{0}", connectNewHeadersResult); return(connectNewHeadersResult); }
/// <summary> /// A new list of headers are presented by a peer, the headers will try to be connected to the tree. /// Blocks associated with headers that are interesting (i.e. represent a chain with greater chainwork than our consensus tip) /// will be requested for download. /// </summary> /// <remarks> /// The headers are assumed to be in consecutive order. /// </remarks> /// <param name="networkPeerId">Id of a peer that presented the headers.</param> /// <param name="headers">The list of headers to connect to the tree.</param> /// <returns> /// Information about which blocks need to be downloaded together with information about which input headers were processed. /// Only headers that we can validate will be processed. The rest of the headers will be submitted later again for processing. /// </returns> public ConnectNewHeadersResult ConnectNewHeaders(int networkPeerId, List <BlockHeader> headers) { Guard.NotNull(headers, nameof(headers)); this.logger.LogTrace("({0}:{1},{2}.{3}:{4})", nameof(networkPeerId), networkPeerId, nameof(headers), nameof(headers.Count), headers.Count); if (!this.chainedHeadersByHash.ContainsKey(headers[0].HashPrevBlock)) { this.logger.LogTrace("(-)[HEADER_COULD_NOT_CONNECT]"); throw new ConnectHeaderException(); } List <ChainedHeader> newChainedHeaders = this.CreateNewHeaders(headers); uint256 lastHash = headers.Last().GetHash(); this.AddOrReplacePeerTip(networkPeerId, lastHash); if (newChainedHeaders == null) { this.logger.LogTrace("(-)[NO_NEW_HEADERS]"); return(new ConnectNewHeadersResult() { Consumed = this.chainedHeadersByHash[lastHash] }); } ChainedHeader earliestNewHeader = newChainedHeaders.First(); ChainedHeader latestNewHeader = newChainedHeaders.Last(); ConnectNewHeadersResult connectNewHeadersResult = null; bool isAssumedValidEnabled = this.consensusSettings.BlockAssumedValid != null; bool isBelowLastCheckpoint = this.consensusSettings.UseCheckpoints && (earliestNewHeader.Height <= this.checkpoints.GetLastCheckpointHeight()); if (isBelowLastCheckpoint || isAssumedValidEnabled) { ChainedHeader currentChainedHeader = latestNewHeader; // When we look for a checkpoint header or an assume valid header, we go from the last presented // header to the first one in the reverse order because if there were multiple checkpoints or a checkpoint // and an assume valid header inside of the presented list of headers, we would only be interested in the last // one as it would cover all previous headers. Reversing the order of processing guarantees that we only need // to deal with one special header, which simplifies the implementation. while (currentChainedHeader != earliestNewHeader) { if (currentChainedHeader.HashBlock == this.consensusSettings.BlockAssumedValid) { this.logger.LogDebug("Chained header '{0}' represents an assumed valid block.", currentChainedHeader); connectNewHeadersResult = this.HandleAssumedValidHeader(currentChainedHeader, latestNewHeader, isBelowLastCheckpoint); break; } CheckpointInfo checkpoint = this.checkpoints.GetCheckpoint(currentChainedHeader.Height); if (checkpoint != null) { this.logger.LogDebug("Chained header '{0}' is a checkpoint.", currentChainedHeader); connectNewHeadersResult = this.HandleCheckpointsHeader(currentChainedHeader, latestNewHeader, checkpoint); break; } currentChainedHeader = currentChainedHeader.Previous; } if ((connectNewHeadersResult == null) && isBelowLastCheckpoint) { connectNewHeadersResult = new ConnectNewHeadersResult() { Consumed = latestNewHeader }; this.logger.LogTrace("Chained header '{0}' below last checkpoint.", currentChainedHeader); } if (connectNewHeadersResult != null) { this.logger.LogTrace("(-)[CHECKPOINT_OR_ASSUMED_VALID]:{0}", connectNewHeadersResult); return(connectNewHeadersResult); } } if (latestNewHeader.ChainWork > this.GetConsensusTip().ChainWork) { this.logger.LogDebug("Chained header '{0}' is the tip of a chain with more work than our current consensus tip.", latestNewHeader); connectNewHeadersResult = this.MarkBetterChainAsRequired(latestNewHeader); } this.logger.LogTrace("(-):{0}", connectNewHeadersResult); return(connectNewHeadersResult); }
private async Task ProcessMessagesRunAsync(CancellationToken cancelToken) { var operation = nameof(this.ProcessMessagesRunAsync); var runStartTimestamp = DateTime.UtcNow; var offset = await this.messageQueue.GetCheckpointedRecordInfoAsync(); if (offset.Index == RecordInfo.InvalidIndex) { offset = offset.Next(); this.inclusive = true; } else { this.inclusive = false; } var fabricErrorsStartedTimestamp = DateTime.Now; var fabricErrorsStarted = false; MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Read", OperationStates.Starting, $"requestOffset: {offset}, inclusive: {inclusive}"); while (!cancelToken.IsCancellationRequested) { var pumpStartTimestamp = DateTime.UtcNow; try { IReadOnlyList <Record <TMessage> > readResult = null; readResult = await this.messageQueue.ReadAsync( offset, this.setting.PartitionSetting.MessagePumpBatchSize, this.inclusive, ReadTimeout, cancelToken); if (readResult == null || readResult.Count == 0) { MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Read", OperationStates.Succeeded, $"requestOffset: {offset}, inclusive: {inclusive}, resultOffset: NULL, resultMessageCount: 0"); } else { var inputMessages = readResult.Select(r => r.Item).ToList(); MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Read", OperationStates.Succeeded, $"requestOffset: {offset}, inclusive: {inclusive}, resultOffset: {readResult[readResult.Count - 1].RecordInfo.Index}, resultMessageCount: {inputMessages.Count}"); if (this.inclusive == true) { this.inclusive = false; } IList <OutputMessage> filteredEvents; try { MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Filter", OperationStates.Starting, $"inputEventCount: {inputMessages.Count}"); filteredEvents = await this.filteringEngine.FilterAsync(inputMessages, cancelToken); MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Filter", OperationStates.Succeeded, $"outputEventCount: {filteredEvents.Count}"); } catch (Exception ex) { MessageDispatcherEventSource.Current.ErrorException(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".Filter", OperationStates.Failed, string.Empty, ex); throw; } var checkpointInfo = new CheckpointInfo(readResult[0].RecordInfo, readResult[readResult.Count - 1].RecordInfo, inputMessages.Count); this.checkpointQueue.Enqueue(checkpointInfo); if (filteredEvents != null && filteredEvents.Count > 0) { await this.ScheduleDeliveryAsync(filteredEvents, checkpointInfo); } else { MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation + ".ScheduleDelivery", OperationStates.Skipping, string.Empty); checkpointInfo.Tcs.SetResult(true); } offset = checkpointInfo.LastRecordInfo; } fabricErrorsStarted = false; } catch (Exception ex) { var runStartElapsed = runStartTimestamp - DateTime.UtcNow; var pumpStartElapsed = pumpStartTimestamp - DateTime.UtcNow; if (runStartElapsed < MinimumRunDurationBeforeFaulting || !ex.IsFabricCriticalException()) { fabricErrorsStarted = false; MessageDispatcherEventSource.Current.Warning(MessageDispatcherEventSource.EmptyTrackingId, this, operation, OperationStates.Failed, $"runStartElapsed={runStartElapsed} pumpStartElapsed={pumpStartElapsed} Waiting for={UnhandledExceptionDelay.TotalMilliseconds} ms Ex={ex.ToString()}"); await TaskHelper.TryDelay(UnhandledExceptionDelay, cancelToken); } else { var fabricErrorsStartedElapsed = fabricErrorsStartedTimestamp - DateTime.UtcNow; var faultMessage = $"MessagePump encountered a critical fabric exception. FabricErrorsStartedElapsed={fabricErrorsStartedElapsed} (Threshold={FabricExceptionsDurationBeforeFaulting}) AND runStartElapsed={runStartElapsed} (Threshold={MinimumRunDurationBeforeFaulting}) pumpStartElapsed={pumpStartElapsed}"; if (!fabricErrorsStarted) { fabricErrorsStarted = true; fabricErrorsStartedTimestamp = DateTime.UtcNow; MessageDispatcherEventSource.Current.Warning(MessageDispatcherEventSource.EmptyTrackingId, this, operation, OperationStates.FailedNotFaulting, faultMessage + $" ex={ex.ToString()}"); } else { if (runStartElapsed > MinimumRunDurationBeforeFaulting && fabricErrorsStartedElapsed > FabricExceptionsDurationBeforeFaulting) { MessageDispatcherEventSource.Current.Error(MessageDispatcherEventSource.EmptyTrackingId, this, operation, OperationStates.Faulting, faultMessage + $" ex={ex.ToString()}"); this.FaultPump(faultMessage, ex); } else { MessageDispatcherEventSource.Current.Warning(MessageDispatcherEventSource.EmptyTrackingId, this, operation, OperationStates.FailedNotFaulting, faultMessage + $" ex={ex.ToString()}"); } } } } } MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, operation, OperationStates.Succeeded, $"requestOffset: {offset}, inclusive: {inclusive}"); }
private async Task ScheduleDeliveryAsync(IList <OutputMessage> outputMessages, CheckpointInfo checkpointInfo) { MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, nameof(this.ScheduleDeliveryAsync), OperationStates.Starting, $"outputMessageCount: {outputMessages.Count}, checkpointInfo: {checkpointInfo}"); var deliverTask = await this.messageDispatcher.DispatchAsync(outputMessages).ConfigureAwait(false); MessageDispatcherEventSource.Current.Info(MessageDispatcherEventSource.EmptyTrackingId, this, nameof(this.ScheduleDeliveryAsync), OperationStates.Succeeded, $"outputMessageCount: {outputMessages.Count}, checkpointInfo: {checkpointInfo}"); this.CompleteScheduleDeliveryAsync(deliverTask, checkpointInfo).Fork(); }