Skip to content

Commit

Permalink
feat: Upgrade BPV to handle vote power
Browse files Browse the repository at this point in the history
  • Loading branch information
OnedgeLee authored and s2quake committed Oct 8, 2024
1 parent 13da229 commit b6d9e6c
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 25 deletions.
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);
}
}
}

0 comments on commit b6d9e6c

Please sign in to comment.