Skip to content

Commit

Permalink
Initial attempt to fix forks
Browse files Browse the repository at this point in the history
  • Loading branch information
jmederosalvarado committed Sep 30, 2022
1 parent 72fe6ba commit 4b7902c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 122 deletions.
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Nethermind.Abi;
Expand All @@ -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
{
Expand All @@ -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,
Expand All @@ -67,7 +59,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 @@ -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<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 (!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;
Expand Down Expand Up @@ -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<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)
// 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<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 == 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);
}
}

Expand All @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

Expand All @@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

Expand All @@ -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<Address>());
private static Keccak GetKey(in long blockNumber) => Keccak.Compute("Validators" + blockNumber);

public ValidatorStore(IDb db)
{
Expand All @@ -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
Expand All @@ -73,15 +81,15 @@ 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)
{
currentValidatorInfo = LoadValidatorInfo(currentValidatorInfo.PreviousFinalizingBlockNumber);
}

return currentValidatorInfo.Validators;
return currentValidatorInfo;
}

private ValidatorInfo GetLatestValidatorInfo()
Expand Down

0 comments on commit 4b7902c

Please sign in to comment.