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);
}
}
}