Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix forks in AuRa #4679

Merged
merged 5 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ public static IEnumerable<TestCaseData> ConsecutiveInitiateChangeData
[TestCaseSource(nameof(ConsecutiveInitiateChangeData))]
public void consecutive_initiate_change_gets_finalized_and_switch_validators(ConsecutiveInitiateChangeTestParameters test)
{
Dictionary<int, int> hashSeeds = new();

Address[] currentValidators = GenerateValidators(1);
SetupInitialValidators(currentValidators);

Expand All @@ -490,20 +492,34 @@ public void consecutive_initiate_change_gets_finalized_and_switch_validators(Con
blockNumber = test.Current.BlockNumber + i;
}

if (hashSeeds.ContainsKey(blockNumber))
hashSeeds[blockNumber]++;
else
hashSeeds[blockNumber] = 0;

_block.Header.Number = blockNumber;
_block.Header.Beneficiary = currentValidators[blockNumber % currentValidators.Length];
_block.Header.AuRaStep = blockNumber;
_block.Header.Hash = Keccak.Compute(blockNumber.ToString());
_block.Header.Hash = Keccak.Compute((blockNumber + hashSeeds[blockNumber]).ToString());
_block.Header.ParentHash = blockNumber == test.StartBlockNumber ? Keccak.Zero : Keccak.Compute((blockNumber - 1 + hashSeeds[blockNumber - 1]).ToString());

TxReceipt[] txReceipts = test.GetReceipts(_validatorContract, _block, _contractAddress, _abiEncoder, SetupAbiAddresses);

Keccak? blockHashForClosure = _block.Hash;
_receiptsStorage.Get(Arg.Is<Block>(b => b.Hash == blockHashForClosure)).Returns(txReceipts);

_block.Header.Bloom = new Bloom(txReceipts.SelectMany(r => r.Logs).ToArray());

_blockTree.FindBlock(_block.Header.Hash, Arg.Any<BlockTreeLookupOptions>()).Returns(new Block(_block.Header.Clone(), BlockBody.Empty));

Action preProcess = () => validator.OnBlockProcessingStart(_block);
preProcess.Should().NotThrow<InvalidOperationException>(test.TestName);
validator.OnBlockProcessingEnd(_block, txReceipts);
int finalizedNumber = blockNumber - validator.Validators.MinSealersForFinalization() + 1;
_blockFinalizationManager.GetLastLevelFinalizedBy(_block.Header.Hash).Returns(finalizedNumber);
_blockFinalizationManager.BlocksFinalized += Raise.EventWith(
new FinalizeEventArgs(_block.Header, Build.A.BlockHeader.WithNumber(finalizedNumber)
.WithHash(Keccak.Compute(finalizedNumber.ToString())).TestObject));
.WithHash(Keccak.Compute((finalizedNumber + hashSeeds[finalizedNumber]).ToString())).TestObject));

currentValidators = test.GetCurrentValidators(blockNumber);
validator.Validators.Should().BeEquivalentTo(currentValidators, o => o.WithStrictOrdering(), $"Validator address should be recognized in block {blockNumber}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Nethermind.Abi;
Expand All @@ -11,15 +10,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
{
Expand All @@ -28,13 +21,13 @@ public partial class ContractBasedValidator : AuRaValidatorBase, IDisposable
private readonly ILogger _logger;

private PendingValidators _currentPendingValidators;
private long _lastProcessedBlockNumber = 0;
private long? _lastProcessedBlockNumber = null;
private Keccak? _lastProcessedBlockHash = null;
private IAuRaBlockFinalizationManager _blockFinalizationManager;
internal IBlockTree BlockTree { get; }
private readonly IReceiptFinder _receiptFinder;

internal IValidatorContract ValidatorContract { get; }
private PendingValidators CurrentPendingValidators => _currentPendingValidators;

public ContractBasedValidator(
IValidatorContract validatorContract,
Expand All @@ -54,7 +47,7 @@ public ContractBasedValidator(
_posdaoTransition = posdaoTransition;
_logger = logManager?.GetClassLogger<ContractBasedValidator>() ?? throw new ArgumentNullException(nameof(logManager));
ValidatorContract = validatorContract ?? throw new ArgumentNullException(nameof(validatorContract));
SetPendingValidators(LoadPendingValidators());
_currentPendingValidators = ValidatorStore.PendingValidators;
SetFinalizationManager(finalizationManager, parentHeader ?? BlockTree.Head?.Header);
}

Expand Down Expand Up @@ -86,37 +79,36 @@ 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 is null || notConsecutiveBlock || isProducingBlock;
var mainChainProcessing = !ForSealing && isProcessingBlock;
bool isInitBlock = InitBlockNumber == block.Number;
bool isProducingBlock = options.ContainsFlag(ProcessingOptions.ProducingBlock);
bool isMainChainProcessing = !ForSealing && !isProducingBlock;
bool isInProcessedRange = _lastProcessedBlockNumber is not null && block.Number - 1 <= _lastProcessedBlockNumber;
jmederosalvarado marked this conversation as resolved.
Show resolved Hide resolved
bool isConsecutiveBlock = _lastProcessedBlockHash is not null && block.ParentHash == _lastProcessedBlockHash;

if (shouldLoadValidators)
// this condition is probably redundant because whenever Validators is null, isConsecutiveBlock will be false
// but let's leave it here just in case, it does not harm
if (Validators is null || !isConsecutiveBlock)
{
Validators = isInitBlock || notConsecutiveBlock
? LoadValidatorsFromContract(BlockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None))
: ValidatorStore.GetValidators();
var parentHeader = BlockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None);
Validators = isInitBlock || !isInProcessedRange ? LoadValidatorsFromContract(parentHeader) : ValidatorStore.GetValidators(block.Number);

if (mainChainProcessing)
if (isMainChainProcessing)
{
if (_logger.IsInfo) _logger.Info($"{(isInitBlock ? "Initial" : "Current")} contract validators ({Validators.Length}): [{string.Join<Address>(", ", Validators)}].");
if (_logger.IsInfo)
_logger.Info($"{(isInitBlock ? "Initial" : "Current")} contract validators ({Validators.Length}): [{string.Join<Address>(", ", Validators)}].");
}
}

if (isInitBlock)
{
if (mainChainProcessing)
if (isMainChainProcessing)
{
ValidatorStore.SetValidators(InitBlockNumber, Validators);
}

InitiateChange(block, Validators.ToArray(), isProcessingBlock, true);
}
else
{
if (mainChainProcessing && notConsecutiveBlock)
if (isMainChainProcessing && !isInProcessedRange)
{
bool loadedValidatorsAreSameInStore = (ValidatorStore.GetValidators()?.SequenceEqual(Validators) == true);
if (!loadedValidatorsAreSameInStore)
Expand All @@ -125,33 +117,22 @@ public override void OnBlockProcessingStart(Block block, ProcessingOptions optio
}
}

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
{
SetPendingValidators(TryGetInitChangeFromPastBlocks(block.ParentHash), true);
}
}
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;
}
else if (!isConsecutiveBlock) // either reorg or blocks skipped (like fast sync)
_currentPendingValidators = ValidatorStore.PendingValidators = TryGetInitChangeFromPastBlocks(block.ParentHash);
}


base.OnBlockProcessingStart(block, options);

FinalizePendingValidatorsIfNeeded(block.Header, isProcessingBlock);
FinalizePendingValidatorsIfNeeded(block.Header, isProducingBlock);

_lastProcessedBlockNumber = block.Number;
(_lastProcessedBlockNumber, _lastProcessedBlockHash) = (block.Number, block.Hash);
}

private PendingValidators TryGetInitChangeFromPastBlocks(Keccak blockHash)
Expand All @@ -167,7 +148,7 @@ private PendingValidators TryGetInitChangeFromPastBlocks(Keccak blockHash)
{
if (Validators.SequenceEqual(potentialValidators))
{
break;
break; // TODO: why this?
LukaszRozmej marked this conversation as resolved.
Show resolved Hide resolved
}

pendingValidators = new PendingValidators(block.Number, block.Hash, potentialValidators);
Expand All @@ -189,40 +170,39 @@ 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<Address>(", ", 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)
{
ValidatorContract.EnsureSystemAccount();
ValidatorContract.FinalizeChange(block);
}
else
// 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.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<Address>(", ", 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 is null && potentialValidators.Length > 0)
var validatorsInfo = ValidatorStore.GetValidatorsInfo(block.Number);
var isInitialValidatorSet = validatorsInfo.FinalizingBlockNumber == InitBlockNumber
&& validatorsInfo.PreviousFinalizingBlockNumber < InitBlockNumber;

if (InitBlockNumber == block.Number || (!isInitialValidatorSet && 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);
}
}

Expand All @@ -241,48 +221,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 is not 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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,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; }
}
}
Loading