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

Upgrade BPV to handle vote power #3959

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion src/Libplanet.Types/Blocks/BlockMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class BlockMetadata : IBlockMetadata
/// <summary>
/// The latest protocol version.
/// </summary>
public const int CurrentProtocolVersion = 9;
public const int CurrentProtocolVersion = 10;

/// <summary>
/// <para>
Expand Down Expand Up @@ -100,6 +100,12 @@ public class BlockMetadata : IBlockMetadata
/// </summary>
public const int EvidenceProtocolVersion = 9;

/// <summary>
/// The starting protocol version where the power of the each validator is stored in the
/// block commit.
/// </summary>
public const int BlockCommitPowerProtocolVersion = 10;

private const string TimestampFormat = "yyyy-MM-ddTHH:mm:ss.ffffffZ";
private static readonly Codec Codec = new Codec();

Expand Down
71 changes: 61 additions & 10 deletions src/Libplanet.Types/Consensus/ValidatorSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Bencodex;
using Libplanet.Crypto;
using Libplanet.Types.Blocks;
using Libplanet.Types.Consensus;

namespace Libplanet.Types.Consensus
{
Expand Down Expand Up @@ -264,16 +263,68 @@ public Validator GetProposer(long height, int round)
/// <remarks>
/// If <see cref="Vote.ValidatorPower"/> is null, power check is ignored.</remarks>
/// <param name="blockCommit">The <see cref="BlockCommit"/> to check.</param>
/// <returns><see langword="true"/> if the <see cref="BlockCommit.Votes"/> is
/// ordered, <see langword="false"/> otherwise.</returns>
public bool ValidateBlockCommitValidators(BlockCommit blockCommit)
/// <exception cref="InvalidBlockCommitException">Thrown when some votes in the
/// <paramref name="blockCommit"/> does not have non-null power.</exception>
/// <exception cref="InvalidBlockCommitException">Thrown when validators from
/// <paramref name="blockCommit"/> is different from validators of this.</exception>
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"));
}
}

/// <summary>
/// Checks whether <see cref="BlockCommit.Votes"/> is ordered
/// by <see cref="Address"/> of each <see cref="Vote.ValidatorPublicKey"/>,
/// and <see cref="Vote.ValidatorPower"/> equals to the one recorded in the chain states.
/// </summary>
/// <remarks>
/// If <see cref="Vote.ValidatorPower"/> is null, power check is ignored.</remarks>
/// <param name="blockCommit">The <see cref="BlockCommit"/> to check.</param>
/// <exception cref="InvalidBlockCommitException">Thrown when some votes in the
/// <paramref name="blockCommit"/> does not have null power.</exception>
/// <exception cref="InvalidBlockCommitException">Thrown when public key of validators from
/// <paramref name="blockCommit"/> is different from validator's public keys of this.
/// </exception>
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"));
}
}
}
}
17 changes: 7 additions & 10 deletions src/Libplanet/Blockchain/BlockChain.Validate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
8 changes: 4 additions & 4 deletions test/Libplanet.Tests/Consensus/ValidatorSetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<InvalidBlockCommitException>(() =>
validatorSet.ValidateBlockCommitValidators(blockCommitWithUnorderedVotes));
Assert.False(
Assert.Throws<InvalidBlockCommitException>(() =>
validatorSet.ValidateBlockCommitValidators(blockCommitWithInvalidPowerVotes));
Assert.False(
Assert.Throws<InvalidBlockCommitException>(() =>
validatorSet.ValidateBlockCommitValidators(blockCommitWithInsufficientVotes));
Assert.True(validatorSet.ValidateBlockCommitValidators(validBlockCommit));
validatorSet.ValidateBlockCommitValidators(validBlockCommit);
}
}
}
Loading