Skip to content

Commit

Permalink
Merge pull request #3882 from planetarium/feature/policy-action
Browse files Browse the repository at this point in the history
PolicyActionsRegistry
  • Loading branch information
limebell committed Jul 22, 2024
2 parents 77be73d + 4acb7e9 commit 318e591
Show file tree
Hide file tree
Showing 60 changed files with 1,425 additions and 243 deletions.
22 changes: 22 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ To be released.

### Backward-incompatible API changes

- (Libplanet) Removed `IBlockPolicy.BlockAction` property. [[#3701]]
- (Libplanet) Added `IBlockPolicy.BeginBlockActions`. property. [[#3701]]
- (Libplanet) Added `IBlockPolicy.EndBlockActions`. property. [[#3701]]
- (Libplanet) Added `IBlockPolicy.BeginTxActions`. property. [[#3748]]
- (Libplanet) Added `IBlockPolicy.EndTxActions`. property. [[#3748]]
- (Libplanet) `BlockPolicy` constructor requires `beginBlockActions`,
`endBlockActions`, `beginTxActions` and `endTxActions` parameters
instead of the `blockAction` parameter.
[[#3701], [#3748]]
- (Libplanet.Action) Renamed `PolicyBlockActionGetter` delegate to
`PolicyActionsGetter` and changed return type to
`ImmutableArray<IAction>`. [[#3701], [#3748]]
- (Libplanet.Action) `ActionEvaluator` constructor requires
`PolicyActionsRegistry` parameter instead of the
`policyBlockActionGetter` parameter. [[#3701], [#3748]]
- (Libplanet.Action) Renamed `IActionContext.BlockAction` property to
`IActionContext.IsPolicyAction`. [[#3764]]

### Backward-incompatible network protocol changes

### Backward-incompatible storage format changes
Expand All @@ -18,6 +36,7 @@ To be released.

- (Libplanet.Store) Added `MerkleTrie.GenerateProof()` method. [[#3870]]
- (Libplanet.Store) Added `MerkleTrie.ValidateProof()` method. [[#3870]]
- (Libplanet.Action) Added `PolicyActionsRegistry` class. [[#3748]]

### Behavioral changes

Expand All @@ -28,6 +47,9 @@ To be released.
### CLI tools

[#3870]: https://github.com/planetarium/libplanet/pull/3870
[#3701]: https://github.com/planetarium/libplanet/pull/3701
[#3748]: https://github.com/planetarium/libplanet/pull/3748
[#3764]: https://github.com/planetarium/libplanet/pull/3764


Version 5.1.2
Expand Down
8 changes: 5 additions & 3 deletions src/Libplanet.Action/ActionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public ActionContext(
int blockProtocolVersion,
IWorld previousState,
int randomSeed,
bool isPolicyAction,
long gasLimit,
IReadOnlyList<ITransaction>? txs = null,
IReadOnlyList<EvidenceBase>? evidence = null)
Expand All @@ -36,6 +37,7 @@ public ActionContext(
BlockProtocolVersion = blockProtocolVersion;
PreviousState = previousState;
RandomSeed = randomSeed;
IsPolicyAction = isPolicyAction;
_gasLimit = gasLimit;
_txs = txs ?? ImmutableList<Transaction>.Empty;
Evidence = evidence ?? ImmutableList<EvidenceBase>.Empty;
Expand Down Expand Up @@ -64,11 +66,11 @@ public ActionContext(
/// <inheritdoc cref="IActionContext.RandomSeed"/>
public int RandomSeed { get; }

/// <inheritdoc cref="IActionContext.BlockAction"/>
public bool BlockAction => TxId is null;
/// <inheritdoc cref="IActionContext.IsPolicyAction"/>
public bool IsPolicyAction { get; }

/// <inheritdoc cref="IActionContext.Txs"/>
public IReadOnlyList<ITransaction> Txs => BlockAction
public IReadOnlyList<ITransaction> Txs => IsPolicyAction
? _txs
: ImmutableList<ITransaction>.Empty;

Expand Down
189 changes: 161 additions & 28 deletions src/Libplanet.Action/ActionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,29 @@ namespace Libplanet.Action
public class ActionEvaluator : IActionEvaluator
{
private readonly ILogger _logger;
private readonly PolicyBlockActionGetter _policyBlockActionGetter;
private readonly PolicyActionsRegistry _policyActionsRegistry;
private readonly IStateStore _stateStore;
private readonly IActionLoader _actionLoader;

/// <summary>
/// Creates a new <see cref="ActionEvaluator"/>.
/// </summary>
/// <param name="policyBlockActionGetter">A delegator to get policy block action to evaluate
/// at the end for each <see cref="IPreEvaluationBlock"/> that gets evaluated.</param>
/// <param name="policyActionsRegistry">
/// A <see cref="PolicyActionsRegistry"/> containing delegators
/// to get policy actions to evaluate at each situation.
/// </param>
/// <param name="stateStore">The <see cref="IStateStore"/> to use to retrieve
/// the states for a provided <see cref="HashDigest{SHA256}"/>.</param>
/// <param name="actionTypeLoader"> A <see cref="IActionLoader"/> implementation using
/// action type lookup.</param>
public ActionEvaluator(
PolicyBlockActionGetter policyBlockActionGetter,
PolicyActionsRegistry policyActionsRegistry,
IStateStore stateStore,
IActionLoader actionTypeLoader)
{
_logger = Log.ForContext<ActionEvaluator>()
.ForContext("Source", nameof(ActionEvaluator));
_policyBlockActionGetter = policyBlockActionGetter;
_policyActionsRegistry = policyActionsRegistry;
_stateStore = stateStore;
_actionLoader = actionTypeLoader;
}
Expand Down Expand Up @@ -121,16 +123,30 @@ public IReadOnlyList<ICommittedActionEvaluation> Evaluate(
IWorld previousState = _stateStore.GetWorld(baseStateRootHash);
previousState = _stateStore.MigrateWorld(previousState, block.ProtocolVersion);

ImmutableList<ActionEvaluation> evaluations =
EvaluateBlock(block, previousState).ToImmutableList();
var evaluations = ImmutableList<ActionEvaluation>.Empty;
if (_policyActionsRegistry.BeginBlockActionsGetter(block) is
{ } beginBlockActions &&
beginBlockActions.Length > 0)
{
evaluations = evaluations.AddRange(EvaluatePolicyBeginBlockActions(
block, previousState
));
previousState = evaluations.Last().OutputState;
}

evaluations = evaluations.AddRange(
EvaluateBlock(block, previousState).ToImmutableList()
);

var policyBlockAction = _policyBlockActionGetter(block);
if (policyBlockAction is { } blockAction)
if (_policyActionsRegistry.EndBlockActionsGetter(block) is { } endBlockActions &&
endBlockActions.Length > 0)
{
previousState = evaluations.Count > 0
? evaluations.Last().OutputState
: previousState;
evaluations = evaluations.Add(EvaluatePolicyBlockAction(block, previousState));
evaluations = evaluations.AddRange(EvaluatePolicyEndBlockActions(
block, previousState
));
}

var committed = ToCommittedEvaluation(block, evaluations, baseStateRootHash);
Expand Down Expand Up @@ -176,6 +192,8 @@ public IReadOnlyList<ICommittedActionEvaluation> Evaluate(
/// being executed.</param>
/// <param name="actions">Actions to evaluate.</param>
/// <param name="stateStore">An <see cref="IStateStore"/> to use.</param>
/// <param name="isPolicyAction">
/// Flag indicates that whether the action is a policy action.</param>
/// <param name="logger">An optional logger.</param>
/// <returns>An enumeration of <see cref="ActionEvaluation"/>s for each
/// <see cref="IAction"/> in <paramref name="actions"/>.
Expand All @@ -187,6 +205,7 @@ internal static IEnumerable<ActionEvaluation> EvaluateActions(
IWorld previousState,
IImmutableList<IAction> actions,
IStateStore stateStore,
bool isPolicyAction,
ILogger? logger = null)
{
IActionContext CreateActionContext(
Expand All @@ -202,6 +221,7 @@ IActionContext CreateActionContext(
blockProtocolVersion: block.ProtocolVersion,
txs: block.Transactions,
previousState: prevState,
isPolicyAction: isPolicyAction,
randomSeed: randomSeed,
gasLimit: actionGasLimit,
evidence: block.Evidence);
Expand All @@ -223,6 +243,7 @@ IActionContext CreateActionContext(
context,
action,
stateStore,
isPolicyAction,
logger);

yield return result.Evaluation;
Expand All @@ -243,6 +264,7 @@ internal static (ActionEvaluation Evaluation, long NextGasLimit) EvaluateAction(
IActionContext context,
IAction action,
IStateStore stateStore,
bool isPolicyAction,
ILogger? logger = null)
{
if (!context.PreviousState.Trie.Recorded)
Expand All @@ -265,10 +287,11 @@ IActionContext CreateActionContext(IWorld newPrevState)
miner: inputContext.Miner,
blockIndex: inputContext.BlockIndex,
blockProtocolVersion: inputContext.BlockProtocolVersion,
txs: inputContext.Txs,
previousState: newPrevState,
randomSeed: inputContext.RandomSeed,
isPolicyAction: isPolicyAction,
gasLimit: inputContext.GasLimit(),
txs: inputContext.Txs,
evidence: inputContext.Evidence);
}

Expand Down Expand Up @@ -459,53 +482,163 @@ internal IEnumerable<ActionEvaluation> EvaluateTx(
ITransaction tx,
IWorld previousState)
{
var evaluations = ImmutableList<ActionEvaluation>.Empty;
if (_policyActionsRegistry.BeginTxActionsGetter(block) is
{ } beginTxActions &&
beginTxActions.Length > 0)
{
evaluations = evaluations.AddRange(
EvaluatePolicyBeginTxActions(block, tx, previousState));
previousState = evaluations.Last().OutputState;
}

ImmutableList<IAction> actions =
ImmutableList.CreateRange(LoadActions(block.Index, tx));
return EvaluateActions(
evaluations = evaluations.AddRange(EvaluateActions(
block: block,
tx: tx,
previousState: previousState,
actions: actions,
stateStore: _stateStore,
logger: _logger);
isPolicyAction: false,
logger: _logger));

if (_policyActionsRegistry.EndTxActionsGetter(block) is
{ } endTxActions &&
endTxActions.Length > 0)
{
previousState = evaluations.Count > 0
? evaluations.Last().OutputState
: previousState;
evaluations = evaluations.AddRange(
EvaluatePolicyEndTxActions(block, tx, previousState));
}

return evaluations;
}

/// <summary>
/// Evaluates the <see cref="IBlockPolicy.BeginBlockActions"/> set by the policy when
/// this <see cref="ActionEvaluator"/> was instantiated for a given
/// <see cref="IPreEvaluationBlockHeader"/>.
/// </summary>
/// <param name="block">The <see cref="IPreEvaluationBlock"/> to evaluate.</param>
/// <param name="previousState">The states immediately before the evaluation of
/// the <see cref="IBlockPolicy.BeginBlockActions"/> held by the instance.</param>
/// <returns>The <see cref="ActionEvaluation"/> of evaluating
/// the <see cref="IBlockPolicy.BeginBlockActions"/> held by the instance
/// for the <paramref name="block"/>.</returns>
[Pure]
internal ActionEvaluation[] EvaluatePolicyBeginBlockActions(
IPreEvaluationBlock block,
IWorld previousState)
{
_logger.Information(
$"Evaluating policy begin block actions for block #{block.Index} " +
$"{ByteUtil.Hex(block.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
block: block,
tx: null,
previousState: previousState,
actions: _policyActionsRegistry.BeginBlockActionsGetter(block),
stateStore: _stateStore,
isPolicyAction: true,
logger: _logger).ToArray();
}

/// <summary>
/// Evaluates the <see cref="IBlockPolicy.EndBlockActions"/> set by the policy when
/// this <see cref="ActionEvaluator"/> was instantiated for a given
/// <see cref="IPreEvaluationBlockHeader"/>.
/// </summary>
/// <param name="block">The <see cref="IPreEvaluationBlock"/> to evaluate.</param>
/// <param name="previousState">The states immediately before the evaluation of
/// the <see cref="IBlockPolicy.EndBlockActions"/> held by the instance.</param>
/// <returns>The <see cref="ActionEvaluation"/> of evaluating
/// the <see cref="IBlockPolicy.EndBlockActions"/> held by the instance
/// for the <paramref name="block"/>.</returns>
[Pure]
internal ActionEvaluation[] EvaluatePolicyEndBlockActions(
IPreEvaluationBlock block,
IWorld previousState)
{
_logger.Information(
$"Evaluating policy end block actions for block #{block.Index} " +
$"{ByteUtil.Hex(block.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
block: block,
tx: null,
previousState: previousState,
actions: _policyActionsRegistry.EndBlockActionsGetter(block),
stateStore: _stateStore,
isPolicyAction: true,
logger: _logger).ToArray();
}

/// <summary>
/// Evaluates the <see cref="IBlockPolicy.BlockAction"/> set by the policy when
/// Evaluates the <see cref="IBlockPolicy.BeginTxActions"/> set by the policy when
/// this <see cref="ActionEvaluator"/> was instantiated for a given
/// <see cref="IPreEvaluationBlockHeader"/>.
/// </summary>
/// <param name="block">The <see cref="IPreEvaluationBlock"/> to evaluate.</param>
/// <param name="transaction">The transaction that the tx action belongs to.</param>
/// <param name="previousState">The states immediately before the evaluation of
/// the <see cref="IBlockPolicy.BlockAction"/> held by the instance.</param>
/// <returns>The <see cref="ActionEvaluation"/> of evaluating
/// the <see cref="IBlockPolicy.BlockAction"/> held by the instance
/// for the <paramref name="block"/>.</returns>
[Pure]
internal ActionEvaluation EvaluatePolicyBlockAction(
internal ActionEvaluation[] EvaluatePolicyBeginTxActions(
IPreEvaluationBlock block,
ITransaction transaction,
IWorld previousState)
{
var policyBlockAction = _policyBlockActionGetter(block);
if (policyBlockAction is null)
{
var message =
"To evaluate policy block action, " +
"policyBlockAction must not be null.";
throw new InvalidOperationException(message);
}
_logger.Information(
$"Evaluating policy begin tx actions for block #{block.Index} " +
$"{ByteUtil.Hex(block.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
block: block,
tx: transaction,
previousState: previousState,
actions: _policyActionsRegistry.BeginTxActionsGetter(block),
stateStore: _stateStore,
isPolicyAction: true,
logger: _logger).ToArray();
}

/// <summary>
/// Evaluates the <see cref="IBlockPolicy.BeginTxActions"/> set by the policy when
/// this <see cref="ActionEvaluator"/> was instantiated for a given
/// <see cref="IPreEvaluationBlockHeader"/>.
/// </summary>
/// <param name="block">The <see cref="IPreEvaluationBlock"/> to evaluate.</param>
/// <param name="transaction">The transaction that the tx action belongs to.</param>
/// <param name="previousState">The states immediately before the evaluation of
/// the <see cref="IBlockPolicy.BlockAction"/> held by the instance.</param>
/// <returns>The <see cref="ActionEvaluation"/> of evaluating
/// the <see cref="IBlockPolicy.BlockAction"/> held by the instance
/// for the <paramref name="block"/>.</returns>
[Pure]
internal ActionEvaluation[] EvaluatePolicyEndTxActions(
IPreEvaluationBlock block,
ITransaction transaction,
IWorld previousState)
{
_logger.Information(
$"Evaluating policy block action for block #{block.Index} " +
$"Evaluating policy end tx actions for block #{block.Index} " +
$"{ByteUtil.Hex(block.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
block: block,
tx: null,
tx: transaction,
previousState: previousState,
actions: new[] { policyBlockAction }.ToImmutableList(),
actions: _policyActionsRegistry.EndTxActionsGetter(block),
stateStore: _stateStore,
logger: _logger).Single();
isPolicyAction: true,
logger: _logger).ToArray();
}

internal IReadOnlyList<ICommittedActionEvaluation>
Expand Down Expand Up @@ -533,7 +666,7 @@ internal IReadOnlyList<ICommittedActionEvaluation>
? evaluation.InputContext.PreviousState.Trie.Hash
: throw new ArgumentException("Trie is not recorded"),
randomSeed: evaluation.InputContext.RandomSeed,
blockAction: evaluation.InputContext.BlockAction),
isPolicyAction: evaluation.InputContext.IsPolicyAction),
outputState: evaluation.OutputState.Trie.Recorded
? evaluation.OutputState.Trie.Hash
: throw new ArgumentException("Trie is not recorded"),
Expand Down
Loading

0 comments on commit 318e591

Please sign in to comment.