diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ContractBasedValidator.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ContractBasedValidator.cs index d4b2f9f057e..297996c5e81 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ContractBasedValidator.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ContractBasedValidator.cs @@ -1,21 +1,20 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Nethermind.Abi; @@ -24,15 +23,9 @@ using Nethermind.Blockchain.Receipts; using Nethermind.Consensus.AuRa.Contracts; using Nethermind.Consensus.Processing; -using Nethermind.Consensus.Transactions; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Evm; -using Nethermind.Evm.Tracing; using Nethermind.Logging; -using Nethermind.Specs.ChainSpecStyle; -using Nethermind.State; -using Nethermind.Db.Blooms; namespace Nethermind.Consensus.AuRa.Validators { @@ -47,7 +40,6 @@ public partial class ContractBasedValidator : AuRaValidatorBase, IDisposable private readonly IReceiptFinder _receiptFinder; internal IValidatorContract ValidatorContract { get; } - private PendingValidators CurrentPendingValidators => _currentPendingValidators; public ContractBasedValidator( IValidatorContract validatorContract, @@ -67,7 +59,7 @@ public ContractBasedValidator( _posdaoTransition = posdaoTransition; _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); ValidatorContract = validatorContract ?? throw new ArgumentNullException(nameof(validatorContract)); - SetPendingValidators(LoadPendingValidators()); + _currentPendingValidators = ValidatorStore.PendingValidators; SetFinalizationManager(finalizationManager, parentHeader ?? BlockTree.Head?.Header); } @@ -99,74 +91,66 @@ public override void OnBlockProcessingStart(Block block, ProcessingOptions optio return; } - var isProducingBlock = options.ContainsFlag(ProcessingOptions.ProducingBlock); - var isProcessingBlock = !isProducingBlock; - var isInitBlock = InitBlockNumber == block.Number; - var notConsecutiveBlock = block.Number - 1 > _lastProcessedBlockNumber || _lastProcessedBlockNumber == 0; - var shouldLoadValidators = Validators == null || notConsecutiveBlock || isProducingBlock; - var mainChainProcessing = !ForSealing && isProcessingBlock; + bool isInitBlock = InitBlockNumber == block.Number; + bool isProducingBlock = options.ContainsFlag(ProcessingOptions.ProducingBlock); + bool isMainChainProcessing = !ForSealing && !isProducingBlock; + bool isConsecutiveBlock = block.Number - 1 == _lastProcessedBlockNumber && _lastProcessedBlockNumber != 0; - if (shouldLoadValidators) + if (Validators == null || !isConsecutiveBlock || isProducingBlock) { - Validators = isInitBlock || notConsecutiveBlock - ? LoadValidatorsFromContract(BlockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None)) - : ValidatorStore.GetValidators(); + var parentHeader = BlockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None); + Validators = isInitBlock || !isConsecutiveBlock ? LoadValidatorsFromContract(parentHeader) : ValidatorStore.GetValidators(); - if (mainChainProcessing) + if (isMainChainProcessing) { - if (_logger.IsInfo) _logger.Info($"{(isInitBlock ? "Initial" : "Current")} contract validators ({Validators.Length}): [{string.Join
(", ", Validators)}]."); + if (_logger.IsInfo) + _logger.Info($"{(isInitBlock ? "Initial" : "Current")} contract validators ({Validators.Length}): [{string.Join
(", ", Validators)}]."); } } if (isInitBlock) { - if (mainChainProcessing) + if (isMainChainProcessing) { ValidatorStore.SetValidators(InitBlockNumber, Validators); } - - InitiateChange(block, Validators.ToArray(), isProcessingBlock, true); } else { - if (mainChainProcessing && notConsecutiveBlock) + if (!isConsecutiveBlock)// either reorg or blocks skipped (like fast sync) { - bool loadedValidatorsAreSameInStore = (ValidatorStore.GetValidators()?.SequenceEqual(Validators) == true); - if (!loadedValidatorsAreSameInStore) + if (isMainChainProcessing) { - ValidatorStore.SetValidators(_blockFinalizationManager.GetLastLevelFinalizedBy(block.ParentHash), Validators); + bool loadedValidatorsAreSameInStore = (ValidatorStore.GetValidators()?.SequenceEqual(Validators) == true); + if (!loadedValidatorsAreSameInStore) + { + ValidatorStore.SetValidators(_blockFinalizationManager.GetLastLevelFinalizedBy(block.ParentHash), Validators); + } } - } - - if (isProcessingBlock) - { - bool reorganisationHappened = block.Number <= _lastProcessedBlockNumber; - if (reorganisationHappened) - { - var reorganisationToBlockBeforePendingValidatorsInitChange = block.Number <= CurrentPendingValidators?.BlockNumber; - SetPendingValidators(reorganisationToBlockBeforePendingValidatorsInitChange ? null : LoadPendingValidators(), reorganisationToBlockBeforePendingValidatorsInitChange); - } - else if (block.Number > _lastProcessedBlockNumber + 1) // blocks skipped, like fast sync + if (!isProducingBlock) { - SetPendingValidators(TryGetInitChangeFromPastBlocks(block.ParentHash), true); + _currentPendingValidators = ValidatorStore.PendingValidators = TryGetInitChangeFromPastBlocks(block.ParentHash); } } - else + + if (isProducingBlock) { - // if we are not processing blocks we are not on consecutive blocks. - // We need to initialize pending validators from db on each block being produced. - SetPendingValidators(LoadPendingValidators()); + // if we are producing blocks we are not on consecutive blocks. + // We need to initialize pending validators from db on each block being produced. + _currentPendingValidators = ValidatorStore.PendingValidators; } } + base.OnBlockProcessingStart(block, options); - FinalizePendingValidatorsIfNeeded(block.Header, isProcessingBlock); + FinalizePendingValidatorsIfNeeded(block.Header, isProducingBlock); _lastProcessedBlockNumber = block.Number; } + // FIXME private PendingValidators TryGetInitChangeFromPastBlocks(Keccak blockHash) { PendingValidators pendingValidators = null; @@ -202,40 +186,36 @@ public override void OnBlockProcessingEnd(Block block, TxReceipt[] receipts, Pro if (ValidatorContract.CheckInitiateChangeEvent(block.Header, receipts, out var potentialValidators)) { - bool isProcessingBlock = !options.ContainsFlag(ProcessingOptions.ProducingBlock); - InitiateChange(block, potentialValidators, isProcessingBlock, Validators.Length == 1); - if (_logger.IsInfo && isProcessingBlock) _logger.Info($"Signal for transition within contract at block {block.ToString(Block.Format.Short)}. New list of {potentialValidators.Length} : [{string.Join
(", ", potentialValidators)}]."); - } - } + bool isProducingBlock = options.ContainsFlag(ProcessingOptions.ProducingBlock); - private void FinalizePendingValidatorsIfNeeded(BlockHeader block, bool isProcessingBlock) - { - if (CurrentPendingValidators?.AreFinalized == true) - { - if (_logger.IsInfo && isProcessingBlock) _logger.Info($"Applying validator set change signalled at block {CurrentPendingValidators.BlockNumber} before block {block.ToString(BlockHeader.Format.Short)}."); - if (block.Number == InitBlockNumber) + // We are ignoring the signal if there are already pending validators. + // This replicates openethereum's behaviour which can be seen as a bug. + if (_currentPendingValidators == null && potentialValidators.Length > 0) { - ValidatorContract.EnsureSystemAccount(); - ValidatorContract.FinalizeChange(block); - } - else - { - ValidatorContract.FinalizeChange(block); + _currentPendingValidators = new PendingValidators(block.Number, block.Hash, potentialValidators); + if (!isProducingBlock) + { + ValidatorStore.PendingValidators = _currentPendingValidators; + + if (_logger.IsInfo) + _logger.Info($"Signal for transition within contract at block {block.ToString(Block.Format.Short)}. New list of {potentialValidators.Length} : [{string.Join
(", ", potentialValidators)}]."); + } } - SetPendingValidators(null, isProcessingBlock); } } - private void InitiateChange(Block block, Address[] potentialValidators, bool isProcessingBlock, bool initiateChangeIsImmediatelyFinalized = false) + private void FinalizePendingValidatorsIfNeeded(BlockHeader block, bool isProducingBlock) { - // We are ignoring the signal if there are already pending validators. This replicates Parity behaviour which can be seen as a bug. - if (CurrentPendingValidators == null && potentialValidators.Length > 0) + var validatorsInfo = ValidatorStore.GetValidatorsInfo(block.Number); + if (block.Number == InitBlockNumber || validatorsInfo.FinalizingBlockNumber == block.Number - 1) { - SetPendingValidators(new PendingValidators(block.Number, block.Hash, potentialValidators) - { - AreFinalized = initiateChangeIsImmediatelyFinalized - }, - !initiateChangeIsImmediatelyFinalized && isProcessingBlock); + if (_logger.IsInfo && !isProducingBlock) + _logger.Info($"Applying validator set change before block {block.ToString(BlockHeader.Format.Short)}."); + + if (block.Number == InitBlockNumber) + ValidatorContract.EnsureSystemAccount(); + + ValidatorContract.FinalizeChange(block); } } @@ -254,48 +234,20 @@ private Address[] LoadValidatorsFromContract(BlockHeader parentHeader) } catch (AbiException e) { - throw new AuRaException($"Failed to initialize validators list on block {parentHeader.ToString(BlockHeader.Format.FullHashAndNumber)} {new StackTrace()}.", e); + throw new AuRaException($"Failed to initialize validators list on block {parentHeader.ToString(BlockHeader.Format.FullHashAndNumber)}\n{new StackTrace()}.", e); } } + // NOTE: this is only added to `_blockFinalizationManager.BlocksFinalized` when `!ForSealing` private void OnBlocksFinalized(object sender, FinalizeEventArgs e) { - if (CurrentPendingValidators != null) - { - // .Any equivalent with for - var currentPendingValidatorsBlockGotFinalized = false; - for (int i = 0; i < e.FinalizedBlocks.Count && !currentPendingValidatorsBlockGotFinalized; i++) - { - currentPendingValidatorsBlockGotFinalized = e.FinalizedBlocks[i].Hash == CurrentPendingValidators.BlockHash; - } - - if (currentPendingValidatorsBlockGotFinalized) - { - CurrentPendingValidators.AreFinalized = true; - Validators = CurrentPendingValidators.Addresses; - SetPendingValidators(CurrentPendingValidators, true); - if (!ForSealing) - { - ValidatorStore.SetValidators(e.FinalizingBlock.Number, Validators); - if (_logger.IsInfo) _logger.Info($"Finalizing validators for transition within contract signalled at block {CurrentPendingValidators.BlockNumber}. after block {e.FinalizingBlock.ToString(BlockHeader.Format.Short)}."); - } - } - } - } - - private PendingValidators LoadPendingValidators() => ValidatorStore.PendingValidators; - - private void SetPendingValidators(PendingValidators validators, bool canSave = false) - { - _currentPendingValidators = validators; - - // We don't want to save to db when: - // * We are producing block - // * We will save later on processing same block (stateDb ignores consecutive calls with same key!) - // * We are loading validators from db. - if (canSave) + if (e.FinalizedBlocks.Any(header => header.Hash == _currentPendingValidators?.BlockHash)) { - ValidatorStore.PendingValidators = validators; + Validators = _currentPendingValidators.Addresses; + ValidatorStore.SetValidators(e.FinalizingBlock.Number, Validators); + if (_logger.IsInfo) + _logger.Info($"Finalizing validators for transition signalled within contract at block {_currentPendingValidators.BlockNumber} after block {e.FinalizingBlock.ToString(BlockHeader.Format.Short)}."); + _currentPendingValidators = ValidatorStore.PendingValidators = null; } } diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/IValidatorStore.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/IValidatorStore.cs index 40074c465da..de766e58059 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/IValidatorStore.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/IValidatorStore.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -21,7 +21,10 @@ namespace Nethermind.Consensus.AuRa.Validators public interface IValidatorStore { void SetValidators(long finalizingBlockNumber, Address[] validators); - Address[] GetValidators(long? blockNumber = null); + + Address[] GetValidators(in long? blockNumber = null); + ValidatorInfo GetValidatorsInfo(in long? blockNumber = null); + PendingValidators PendingValidators { get; set; } } } diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorStore.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorStore.cs index 3fb875783d6..16f78d8529e 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorStore.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorStore.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -34,6 +34,7 @@ public class ValidatorStore : IValidatorStore private ValidatorInfo _latestValidatorInfo; private static readonly int EmptyBlockNumber = -1; private static readonly ValidatorInfo EmptyValidatorInfo = new ValidatorInfo(-1, -1, Array.Empty
()); + private static Keccak GetKey(in long blockNumber) => Keccak.Compute("Validators" + blockNumber); public ValidatorStore(IDb db) { @@ -55,12 +56,19 @@ public void SetValidators(long finalizingBlockNumber, Address[] validators) } } - private Keccak GetKey(in long blockNumber) => Keccak.Compute("Validators" + blockNumber); + public Address[] GetValidators(in long? blockNumber = null) + { + return blockNumber == null || blockNumber > _latestFinalizedValidatorsBlockNumber + ? GetLatestValidatorInfo().Validators + : FindValidatorInfo(blockNumber.Value).Validators; + } - public Address[] GetValidators(long? blockNumber = null) + public ValidatorInfo GetValidatorsInfo(in long? blockNumber = null) { - return blockNumber == null || blockNumber > _latestFinalizedValidatorsBlockNumber ? GetLatestValidatorInfo().Validators : FindValidatorInfo(blockNumber.Value); + return blockNumber == null || blockNumber > _latestFinalizedValidatorsBlockNumber + ? GetLatestValidatorInfo() + : FindValidatorInfo(blockNumber.Value); } public PendingValidators PendingValidators @@ -73,7 +81,7 @@ public PendingValidators PendingValidators set => _db.Set(PendingValidatorsKey, PendingValidatorsDecoder.Encode(value).Bytes); } - private Address[] FindValidatorInfo(in long blockNumber) + private ValidatorInfo FindValidatorInfo(in long blockNumber) { var currentValidatorInfo = GetLatestValidatorInfo(); while (currentValidatorInfo.FinalizingBlockNumber >= blockNumber) @@ -81,7 +89,7 @@ private Address[] FindValidatorInfo(in long blockNumber) currentValidatorInfo = LoadValidatorInfo(currentValidatorInfo.PreviousFinalizingBlockNumber); } - return currentValidatorInfo.Validators; + return currentValidatorInfo; } private ValidatorInfo GetLatestValidatorInfo()