/// <summary>Rewinds the connected part of invalid chain.</summary> private async Task RewindPartiallyConnectedChainAsync(ChainedHeader lastValidatedBlockHeader, ChainedHeader currentTip) { this.logger.LogTrace("({0}:'{1}',{2}:'{3}'", nameof(lastValidatedBlockHeader), lastValidatedBlockHeader, nameof(currentTip), currentTip); ChainedHeader current = lastValidatedBlockHeader; while (currentTip.Height < current.Height) { RewindState transitionState = await this.consensusRules.RewindAsync().ConfigureAwait(false); lock (this.peerLock) { current = this.chainedHeaderTree.GetChainedHeader(transitionState.BlockHash); } } if (currentTip.Height != current.Height) { // The rewind operation must return to the same fork point. this.logger.LogError("The rewind operation ended up at '{0}' instead of fork point '{1}'.", currentTip, current); this.logger.LogTrace("(-)[INVALID_REWIND]"); throw new ConsensusException("The rewind operation must return to the same fork point."); } this.logger.LogTrace("(-)"); }
/// <summary>Rewinds to fork point or below it.</summary> /// <returns>New consensus tip.</returns> private async Task <ChainedHeader> RewindToForkPointOrBelowAsync(ChainedHeader fork, ChainedHeader oldTip) { this.logger.LogTrace("({0}:'{1}',{2}:'{3}'", nameof(fork), fork, nameof(oldTip), oldTip); ChainedHeader currentTip = oldTip; while (fork.Height < currentTip.Height) { RewindState transitionState = await this.consensusRules.RewindAsync().ConfigureAwait(false); lock (this.peerLock) { currentTip = this.chainedHeaderTree.GetChainedHeader(transitionState.BlockHash); } } this.logger.LogTrace("(-):'{0}'", currentTip); return(currentTip); }
/// <inheritdoc /> /// <remarks> /// If <see cref="blockStore"/> is not <c>null</c> (block store is available) then all block headers in /// <see cref="chainedHeaderTree"/> will be marked as their block data is available. /// If store is not available the <see cref="ConsensusManager"/> won't be able to serve blocks from disk, /// instead all block requests that are not in memory will be sent to the <see cref="blockPuller"/>. /// </remarks> public async Task InitializeAsync(ChainedHeader chainTip) { this.logger.LogTrace("({0}:'{1}')", nameof(chainTip), chainTip); // TODO: consensus store // We should consider creating a consensus store class that will internally contain // coinview and it will abstract the methods `RewindAsync()` `GetBlockHashAsync()` uint256 consensusTipHash = await this.consensusRules.GetBlockHashAsync().ConfigureAwait(false); while (true) { this.Tip = chainTip.FindAncestorOrSelf(consensusTipHash); if (this.Tip?.HashBlock == consensusTipHash) { break; } // In case block store initialized behind, rewind until or before the block store tip. // The node will complete loading before connecting to peers so the chain will never know if a reorg happened. RewindState transitionState = await this.consensusRules.RewindAsync().ConfigureAwait(false); consensusTipHash = transitionState.BlockHash; } this.chainState.ConsensusTip = this.Tip; this.chainedHeaderTree.Initialize(this.Tip, this.blockStore != null); this.blockPuller.Initialize(); this.isIbd = this.ibdState.IsInitialBlockDownload(); this.blockPuller.OnIbdStateChanged(this.isIbd); this.logger.LogTrace("(-)"); }