From b6d9e6c6b788307613502d6c7d85748623e85f42 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 7 Oct 2024 11:27:53 +0900 Subject: [PATCH] feat: Upgrade BPV to handle vote power --- src/Libplanet.Types/Blocks/BlockMetadata.cs | 8 ++- src/Libplanet.Types/Consensus/ValidatorSet.cs | 71 ++++++++++++++++--- .../Blockchain/BlockChain.Validate.cs | 17 ++--- .../Consensus/ValidatorSetTest.cs | 8 +-- 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/Libplanet.Types/Blocks/BlockMetadata.cs b/src/Libplanet.Types/Blocks/BlockMetadata.cs index 23d239b0a99..6297166a203 100644 --- a/src/Libplanet.Types/Blocks/BlockMetadata.cs +++ b/src/Libplanet.Types/Blocks/BlockMetadata.cs @@ -23,7 +23,7 @@ public class BlockMetadata : IBlockMetadata /// /// The latest protocol version. /// - public const int CurrentProtocolVersion = 9; + public const int CurrentProtocolVersion = 10; /// /// @@ -100,6 +100,12 @@ public class BlockMetadata : IBlockMetadata /// public const int EvidenceProtocolVersion = 9; + /// + /// The starting protocol version where the power of the each validator is stored in the + /// block commit. + /// + public const int BlockCommitPowerProtocolVersion = 10; + private const string TimestampFormat = "yyyy-MM-ddTHH:mm:ss.ffffffZ"; private static readonly Codec Codec = new Codec(); diff --git a/src/Libplanet.Types/Consensus/ValidatorSet.cs b/src/Libplanet.Types/Consensus/ValidatorSet.cs index 849571ad7a2..f323e222fc8 100644 --- a/src/Libplanet.Types/Consensus/ValidatorSet.cs +++ b/src/Libplanet.Types/Consensus/ValidatorSet.cs @@ -7,7 +7,6 @@ using Bencodex; using Libplanet.Crypto; using Libplanet.Types.Blocks; -using Libplanet.Types.Consensus; namespace Libplanet.Types.Consensus { @@ -264,16 +263,68 @@ public Validator GetProposer(long height, int round) /// /// If is null, power check is ignored. /// The to check. - /// if the is - /// ordered, otherwise. - public bool ValidateBlockCommitValidators(BlockCommit blockCommit) + /// Thrown when some votes in the + /// does not have non-null power. + /// Thrown when validators from + /// is different from validators of this. + public void ValidateBlockCommitValidators(BlockCommit blockCommit) { - return Validators.Select(validator => validator.PublicKey) - .SequenceEqual( - blockCommit.Votes.Select(vote => vote.ValidatorPublicKey).ToList()) && - blockCommit.Votes.All( - v => v.ValidatorPower is null || - v.ValidatorPower == GetValidator(v.ValidatorPublicKey).Power); + ValidatorSet validatorSetFromCommit = new ValidatorSet(blockCommit.Votes.Select( + v => v.ValidatorPower is BigInteger power + ? new Validator(v.ValidatorPublicKey, power) + : throw new InvalidBlockCommitException( + "All votes in the block commit after block protocol version 10 " + + "must have power.")).ToList()); + + if (!Equals(validatorSetFromCommit)) + { + throw new InvalidBlockCommitException( + $"BlockCommit of BlockHash {blockCommit.BlockHash} " + + $"has different validator set with chain state's validator set: \n" + + $"in states | \n " + + Validators.Aggregate( + string.Empty, (s, v) => s + v + ", \n") + + $"in blockCommit | \n " + + validatorSetFromCommit.Validators.Aggregate( + string.Empty, (s, v) => s + v + ", \n")); + } + } + + /// + /// Checks whether is ordered + /// by of each , + /// and equals to the one recorded in the chain states. + /// + /// + /// If is null, power check is ignored. + /// The to check. + /// Thrown when some votes in the + /// does not have null power. + /// Thrown when public key of validators from + /// is different from validator's public keys of this. + /// + public void ValidateLegacyBlockCommitValidators(BlockCommit blockCommit) + { + if (blockCommit.Votes.Any(v => v.ValidatorPower is not null)) + { + throw new InvalidBlockCommitException( + "All votes in the block commit before block protocol version 10 " + + "must have null power."); + } + + if (!Validators.Select(validator => validator.PublicKey).SequenceEqual( + blockCommit.Votes.Select(vote => vote.ValidatorPublicKey).ToList())) + { + throw new InvalidBlockCommitException( + $"BlockCommit of BlockHash {blockCommit.BlockHash} " + + $"has different validator set with chain state's validator set: \n" + + $"in states | \n " + + Validators.Aggregate( + string.Empty, (s, key) => s + key + ", \n") + + $"in blockCommit | \n " + + blockCommit.Votes.Aggregate( + string.Empty, (s, key) => s + key.ValidatorPublicKey + ", \n")); + } } } } diff --git a/src/Libplanet/Blockchain/BlockChain.Validate.cs b/src/Libplanet/Blockchain/BlockChain.Validate.cs index c471fb5661b..f4a8a4126e8 100644 --- a/src/Libplanet/Blockchain/BlockChain.Validate.cs +++ b/src/Libplanet/Blockchain/BlockChain.Validate.cs @@ -144,17 +144,14 @@ internal void ValidateBlockCommit( var validators = block.ProtocolVersion < BlockMetadata.SlothProtocolVersion ? GetWorldState(block.PreviousHash ?? Genesis.Hash).GetValidatorSet() : GetWorldState(block.StateRootHash).GetValidatorSet(); - if (!validators.ValidateBlockCommitValidators(blockCommit)) + + if (block.ProtocolVersion < BlockMetadata.BlockCommitPowerProtocolVersion) { - throw new InvalidBlockCommitException( - $"BlockCommit of BlockHash {blockCommit.BlockHash} " + - $"has different validator set with chain state's validator set: \n" + - $"in states | \n " + - validators.Validators.Aggregate( - string.Empty, (s, key) => s + key + ", \n") + - $"in blockCommit | \n " + - blockCommit.Votes.Aggregate( - string.Empty, (s, key) => s + key.ValidatorPublicKey + ", \n")); + validators.ValidateLegacyBlockCommitValidators(blockCommit); + } + else + { + validators.ValidateBlockCommitValidators(blockCommit); } BigInteger commitPower = blockCommit.Votes.Aggregate( diff --git a/test/Libplanet.Tests/Consensus/ValidatorSetTest.cs b/test/Libplanet.Tests/Consensus/ValidatorSetTest.cs index 676445e93a4..07136c03fa5 100644 --- a/test/Libplanet.Tests/Consensus/ValidatorSetTest.cs +++ b/test/Libplanet.Tests/Consensus/ValidatorSetTest.cs @@ -172,13 +172,13 @@ public void ValidateBlockCommitValidators() new BlockCommit(height, round, hash, orderedVotes.Take(5).ToImmutableArray()); var validBlockCommit = new BlockCommit(height, round, hash, orderedVotes); - Assert.False( + Assert.Throws(() => validatorSet.ValidateBlockCommitValidators(blockCommitWithUnorderedVotes)); - Assert.False( + Assert.Throws(() => validatorSet.ValidateBlockCommitValidators(blockCommitWithInvalidPowerVotes)); - Assert.False( + Assert.Throws(() => validatorSet.ValidateBlockCommitValidators(blockCommitWithInsufficientVotes)); - Assert.True(validatorSet.ValidateBlockCommitValidators(validBlockCommit)); + validatorSet.ValidateBlockCommitValidators(validBlockCommit); } } }