Skip to content

Commit

Permalink
Moved IWorld interpretation to a separate class
Browse files Browse the repository at this point in the history
  • Loading branch information
greymistcube committed May 2, 2024
1 parent 0e5d1cf commit 308c3c3
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 116 deletions.
116 changes: 3 additions & 113 deletions Libplanet.Action/ActionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
using Libplanet.Action.Loader;
using Libplanet.Action.State;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Types.Blocks;
using Libplanet.Types.Tx;
using Serilog;
using static Libplanet.Action.State.KeyConverters;

namespace Libplanet.Action
{
Expand Down Expand Up @@ -110,8 +108,8 @@ public IReadOnlyList<ICommittedActionEvaluation> Evaluate(
stopwatch.Start();
try
{
IWorld previousState = PrepareInitialDelta(baseStateRootHash);
previousState = MigrateWorld(previousState, block.ProtocolVersion);
IWorld previousState = _stateStore.GetWorld(baseStateRootHash);
previousState = _stateStore.MigrateWorld(previousState, block.ProtocolVersion);

ImmutableList<ActionEvaluation> evaluations =
EvaluateBlock(block, previousState).ToImmutableList();
Expand Down Expand Up @@ -333,15 +331,7 @@ IActionContext CreateActionContext(IWorld newPrevState)

state = feeCollector.Refund(state);
state = feeCollector.Reward(state);

if (state.Legacy)
{
state = CommitLegacyWorld(state, stateStore);
}
else
{
state = CommitWorld(state, stateStore);
}
state = stateStore.CommitWorld(state);

if (!state.Trie.Recorded)
{
Expand Down Expand Up @@ -549,106 +539,6 @@ internal IReadOnlyList<ICommittedActionEvaluation>
return committedEvaluations;
}

internal IWorld MigrateWorld(IWorld originalWorld, int targetVersion)
{
if (originalWorld.Version > targetVersion)
{
throw new ApplicationException(
$"Given {nameof(originalWorld)} with version {originalWorld.Version} " +
$"cannot be migrated to a lower version {targetVersion}.");
}

IWorld world = originalWorld;

// Migrate up to BlockMetadata.WorldStateProtocolVersion
// if conditions are met.
if (targetVersion >= BlockMetadata.WorldStateProtocolVersion &&
world.Version < BlockMetadata.WorldStateProtocolVersion)
{
var worldTrie = _stateStore.GetStateRoot(null);
worldTrie = worldTrie.SetMetadata(
new TrieMetadata(BlockMetadata.WorldStateProtocolVersion));
worldTrie = worldTrie.Set(
ToStateKey(ReservedAddresses.LegacyAccount),
new Binary(world.Trie.Hash.ByteArray));
worldTrie = _stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, _stateStore));
}

// Migrate up to BlockMetadata.ValidatorSetAccountProtocolVersion
// if conditions are met.
if (targetVersion >= BlockMetadata.ValidatorSetAccountProtocolVersion &&
world.Version < BlockMetadata.ValidatorSetAccountProtocolVersion)
{
var worldTrie = world.Trie;
worldTrie = worldTrie.SetMetadata(
new TrieMetadata(BlockMetadata.ValidatorSetAccountProtocolVersion));
worldTrie = _stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, _stateStore));

var legacyAccountTrie =
world.GetAccount(ReservedAddresses.LegacyAccount).Trie;
IValue? rawValidatorSet = legacyAccountTrie.Get(ValidatorSetKey);

// Move encoded validator set only if it already exists.
if (rawValidatorSet is { } rawValue)
{
legacyAccountTrie = legacyAccountTrie.Remove(ValidatorSetKey);
legacyAccountTrie = _stateStore.Commit(legacyAccountTrie);

var validatorSetAccountTrie =
world.GetAccount(ReservedAddresses.ValidatorSetAccount).Trie;
validatorSetAccountTrie = validatorSetAccountTrie.Set(
ToStateKey(ValidatorSetAccount.ValidatorSetAddress),
rawValue);
validatorSetAccountTrie = _stateStore.Commit(validatorSetAccountTrie);

worldTrie = worldTrie.Set(
ToStateKey(ReservedAddresses.LegacyAccount),
new Binary(legacyAccountTrie.Hash.ByteArray));
worldTrie = worldTrie.Set(
ToStateKey(ReservedAddresses.ValidatorSetAccount),
new Binary(validatorSetAccountTrie.Hash.ByteArray));
worldTrie = _stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, _stateStore));
}
}

// Migrate up to target version if conditions are met.
if (targetVersion >= BlockMetadata.WorldStateProtocolVersion &&
world.Version < targetVersion)
{
var worldTrie = world.Trie;
worldTrie = worldTrie.SetMetadata(new TrieMetadata(targetVersion));
worldTrie = _stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, _stateStore));
}

return world;
}

private static IWorld CommitLegacyWorld(IWorld prevWorld, IStateStore stateStore)
{
return new World(
new WorldBaseState(
stateStore.Commit(prevWorld.GetAccount(ReservedAddresses.LegacyAccount).Trie),
stateStore));
}

private static IWorld CommitWorld(IWorld prevWorld, IStateStore stateStore)
{
var worldTrie = prevWorld.Trie;
foreach (var account in prevWorld.Delta.Accounts)
{
var accountTrie = stateStore.Commit(account.Value.Trie);
worldTrie = worldTrie.Set(
ToStateKey(account.Key), new Binary(accountTrie.Hash.ByteArray));
}

return new World(
new WorldBaseState(stateStore.Commit(worldTrie), stateStore));
}

[Pure]
private static IEnumerable<ITransaction> OrderTxsForEvaluationV0(
IEnumerable<ITransaction> txs,
Expand Down
168 changes: 168 additions & 0 deletions Libplanet.Action/State/IStateStoreExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System;
using System.Security.Cryptography;
using Bencodex.Types;
using Libplanet.Common;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Types.Blocks;

namespace Libplanet.Action.State
{
internal static class IStateStoreExtensions
{
/// <summary>
/// Retrieves the <see cref="IWorld"/> associated with
/// given <paramref name="stateRootHash"/>.
/// </summary>
/// <param name="stateStore">The <see cref="IStateStore"/> to retrieve
/// an <see cref="IWorld"/> from.</param>
/// <param name="stateRootHash">The state root hash of the <see cref="IWorld"/>
/// to retrieve.</param>
/// <returns>The <see cref="IWorld"/> associated with
/// given <paramref name="stateRootHash"/>.</returns>
internal static IWorld GetWorld(
this IStateStore stateStore,
HashDigest<SHA256>? stateRootHash)
{
return new World(
new WorldBaseState(stateStore.GetStateRoot(stateRootHash), stateStore));
}

/// <summary>
/// Commits given <paramref name="world"/> to given <paramref name="stateStore"/>.
/// </summary>
/// <param name="stateStore">The <see cref="IStateStore"/> to commit
/// <paramref name="world"/> to.</param>
/// <param name="world">The <see cref="IWorld"/> to commit.</param>
/// <returns>The committed <see cref="IWorld"/>.</returns>
internal static IWorld CommitWorld(this IStateStore stateStore, IWorld world)
{
if (world.Version >= BlockMetadata.WorldStateProtocolVersion)
{
var worldTrie = world.Trie;
foreach (var account in world.Delta.Accounts)
{
var accountTrie = stateStore.Commit(account.Value.Trie);
worldTrie = worldTrie.Set(
KeyConverters.ToStateKey(account.Key),
new Binary(accountTrie.Hash.ByteArray));
}

return new World(
new WorldBaseState(stateStore.Commit(worldTrie), stateStore));
}
else
{
return new World(
new WorldBaseState(
stateStore.Commit(world.GetAccount(ReservedAddresses.LegacyAccount).Trie),
stateStore));
}
}

/// <summary>
/// Migrates given <paramref name="world"/> to <paramref name="targetVersion"/>.
/// </summary>
/// <param name="stateStore">The <see cref="IStateStore"/> to commit
/// the migrated <see cref="IWorld"/>.</param>
/// <param name="world">The <see cref="IWorld"/> to migrate.</param>
/// <param name="targetVersion">The target <see cref="IWorld"/> version
/// to migrate to.</param>
/// <returns>The migrated <see cref="IWorld"/> of <paramref name="world"/> with
/// <see cref="IWorld.Version"/> equal to:
/// <list type="bullet">
/// <item><description>
/// zero if <paramref name="targetVersion"/> is less than
/// <see cref="BlockMetadata.WorldStateProtocolVersion"/>.
/// </description></item>
/// <item><description>
/// <paramref name="targetVersion"/> if <paramref name="targetVersion"/> is
/// greater than or equal to <see cref="BlockMetadata.WorldStateProtocolVersion"/>.
/// </description></item>
/// </list>
/// </returns>
/// <exception cref="ApplicationException">Thrown when <paramref name="targetVersion"/> is
/// lower than the <see cref="IWorld.Version"/> of <paramref name="world"/>.</exception>
/// <remarks>
/// Migrated <see cref="IWorld"/> is automatically committed before returning.
/// </remarks>
internal static IWorld MigrateWorld(
this IStateStore stateStore,
IWorld world,
int targetVersion)
{
if (world.Version > targetVersion)
{
throw new ApplicationException(
$"Given {nameof(world)} with version {world.Version} " +
$"cannot be migrated to a lower version {targetVersion}.");
}

// Migrate up to BlockMetadata.WorldStateProtocolVersion
// if conditions are met.
if (targetVersion >= BlockMetadata.WorldStateProtocolVersion &&
world.Version < BlockMetadata.WorldStateProtocolVersion)
{
var worldTrie = stateStore.GetStateRoot(null);
worldTrie = worldTrie.SetMetadata(
new TrieMetadata(BlockMetadata.WorldStateProtocolVersion));
worldTrie = worldTrie.Set(
KeyConverters.ToStateKey(ReservedAddresses.LegacyAccount),
new Binary(world.Trie.Hash.ByteArray));
worldTrie = stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, stateStore));
}

// Migrate up to BlockMetadata.ValidatorSetAccountProtocolVersion
// if conditions are met.
if (targetVersion >= BlockMetadata.ValidatorSetAccountProtocolVersion &&
world.Version < BlockMetadata.ValidatorSetAccountProtocolVersion)
{
var worldTrie = world.Trie;
worldTrie = worldTrie.SetMetadata(
new TrieMetadata(BlockMetadata.ValidatorSetAccountProtocolVersion));
worldTrie = stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, stateStore));

var legacyAccountTrie =
world.GetAccount(ReservedAddresses.LegacyAccount).Trie;
IValue? rawValidatorSet = legacyAccountTrie.Get(KeyConverters.ValidatorSetKey);

// Move encoded validator set only if it already exists.
if (rawValidatorSet is { } rawValue)
{
legacyAccountTrie = legacyAccountTrie.Remove(KeyConverters.ValidatorSetKey);
legacyAccountTrie = stateStore.Commit(legacyAccountTrie);

var validatorSetAccountTrie =
world.GetAccount(ReservedAddresses.ValidatorSetAccount).Trie;
validatorSetAccountTrie = validatorSetAccountTrie.Set(
KeyConverters.ToStateKey(ValidatorSetAccount.ValidatorSetAddress),
rawValue);
validatorSetAccountTrie = stateStore.Commit(validatorSetAccountTrie);

worldTrie = worldTrie.Set(
KeyConverters.ToStateKey(ReservedAddresses.LegacyAccount),
new Binary(legacyAccountTrie.Hash.ByteArray));
worldTrie = worldTrie.Set(
KeyConverters.ToStateKey(ReservedAddresses.ValidatorSetAccount),
new Binary(validatorSetAccountTrie.Hash.ByteArray));
worldTrie = stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, stateStore));
}
}

// Migrate up to target version if conditions are met.
if (targetVersion >= BlockMetadata.WorldStateProtocolVersion &&
world.Version < targetVersion)
{
var worldTrie = world.Trie;
worldTrie = worldTrie.SetMetadata(new TrieMetadata(targetVersion));
worldTrie = stateStore.Commit(worldTrie);
world = new World(new WorldBaseState(worldTrie, stateStore));
}

return world;
}
}
}
6 changes: 3 additions & 3 deletions Libplanet.Tests/Action/ActionEvaluatorTest.Migration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void MigrateWorldWithValidatorSet()
trie0 = stateStore.Commit(trie0);
var world0 = new World(new WorldBaseState(trie0, stateStore));

var world4 = actionEvaluator.MigrateWorld(
var world4 = stateStore.MigrateWorld(
world0, BlockMetadata.PBFTProtocolVersion);
Assert.True(world4.Trie.Recorded);
Assert.Equal(0, world4.Version);
Expand All @@ -60,7 +60,7 @@ public void MigrateWorldWithValidatorSet()
validatorSet,
world4.GetValidatorSet());

var world5 = actionEvaluator.MigrateWorld(
var world5 = stateStore.MigrateWorld(
world0, BlockMetadata.WorldStateProtocolVersion);
Assert.True(world5.Trie.Recorded);
Assert.Equal(5, world5.Version);
Expand All @@ -78,7 +78,7 @@ public void MigrateWorldWithValidatorSet()
.Get(KeyConverters.ValidatorSetKey));
Assert.Equal(validatorSet, world5.GetValidatorSet());

var world6 = actionEvaluator.MigrateWorld(
var world6 = stateStore.MigrateWorld(
world0, BlockMetadata.ValidatorSetAccountProtocolVersion);
Assert.True(world6.Trie.Recorded);
Assert.Equal(6, world6.Version);
Expand Down

0 comments on commit 308c3c3

Please sign in to comment.