Skip to content

Commit

Permalink
feat: Add begin/end block actions.
Browse files Browse the repository at this point in the history
  • Loading branch information
s2quake committed Mar 21, 2024
1 parent 13f3e50 commit 8baee41
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 32 deletions.
84 changes: 62 additions & 22 deletions Libplanet.Action/ActionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,34 @@ namespace Libplanet.Action
public class ActionEvaluator : IActionEvaluator
{
private readonly ILogger _logger;
private readonly PolicyBlockActionGetter _policyBlockActionGetter;
private readonly PolicyBlockActionGetter _policyBeginBlockActionGetter;
private readonly PolicyBlockActionGetter _policyEndBlockActionGetter;
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="policyBeginBlockActionGetter">A delegator to get policy block actions to
/// evaluate at the beginning for each <see cref="IPreEvaluationBlock"/>
/// that gets evaluated.</param>
/// <param name="policyEndBlockActionGetter">A delegator to get policy block actions to
/// evaluate at the end for each <see cref="IPreEvaluationBlock"/> that gets evaluated.
/// </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,
PolicyBlockActionGetter policyBeginBlockActionGetter,
PolicyBlockActionGetter policyEndBlockActionGetter,
IStateStore stateStore,
IActionLoader actionTypeLoader)
{
_logger = Log.ForContext<ActionEvaluator>()
.ForContext("Source", nameof(ActionEvaluator));
_policyBlockActionGetter = policyBlockActionGetter;
_policyBeginBlockActionGetter = policyBeginBlockActionGetter;
_policyEndBlockActionGetter = policyEndBlockActionGetter;
_stateStore = stateStore;
_actionLoader = actionTypeLoader;
}
Expand Down Expand Up @@ -123,16 +130,29 @@ public IReadOnlyList<ICommittedActionEvaluation> Evaluate(
throw new ApplicationException("World cannot be mutated from modern to legacy");
}

ImmutableList<ActionEvaluation> evaluations =
EvaluateBlock(block, previousState).ToImmutableList();
var evaluations = ImmutableList<ActionEvaluation>.Empty;
if (_policyBeginBlockActionGetter(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 (_policyEndBlockActionGetter(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 @@ -491,30 +511,50 @@ internal IEnumerable<ActionEvaluation> EvaluateTx(
/// the <see cref="IBlockPolicy.BlockAction"/> held by the instance
/// for the <paramref name="blockHeader"/>.</returns>
[Pure]
internal ActionEvaluation EvaluatePolicyBlockAction(
internal ActionEvaluation[] EvaluatePolicyBeginBlockActions(
IPreEvaluationBlockHeader blockHeader,
IWorld previousState)
{
var policyBlockAction = _policyBlockActionGetter(blockHeader);
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 block actions for block #{blockHeader.Index} " +
$"{ByteUtil.Hex(blockHeader.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
blockHeader: blockHeader,
tx: null,
previousState: previousState,
actions: _policyBeginBlockActionGetter(blockHeader),
stateStore: _stateStore,
logger: _logger).ToArray();
}

/// <summary>
/// Evaluates the <see cref="IBlockPolicy.BlockAction"/> set by the policy when
/// this <see cref="ActionEvaluator"/> was instantiated for a given
/// <see cref="IPreEvaluationBlockHeader"/>.
/// </summary>
/// <param name="blockHeader">The header of the block to evaluate.</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="blockHeader"/>.</returns>
[Pure]
internal ActionEvaluation[] EvaluatePolicyEndBlockActions(
IPreEvaluationBlockHeader blockHeader,
IWorld previousState)
{
_logger.Information(
$"Evaluating policy block action for block #{blockHeader.Index} " +
$"Evaluating policy end block actions for block #{blockHeader.Index} " +
$"{ByteUtil.Hex(blockHeader.PreEvaluationHash.ByteArray)}");

return EvaluateActions(
blockHeader: blockHeader,
tx: null,
previousState: previousState,
actions: new[] { policyBlockAction }.ToImmutableList(),
actions: _policyEndBlockActionGetter(blockHeader),
stateStore: _stateStore,
logger: _logger).Single();
logger: _logger).ToArray();
}

internal IWorld PrepareInitialDelta(HashDigest<SHA256>? stateRootHash)
Expand Down
5 changes: 4 additions & 1 deletion Libplanet.Action/PolicyBlockActionGetter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System.Collections.Immutable;
using Libplanet.Types.Blocks;

namespace Libplanet.Action
{
public delegate IAction? PolicyBlockActionGetter(IPreEvaluationBlockHeader blockHeader);
public delegate ImmutableArray<IAction> PolicyBlockActionGetter(
IPreEvaluationBlockHeader blockHeader
);
}
3 changes: 2 additions & 1 deletion Libplanet/Blockchain/BlockChain.Evaluate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ public IReadOnlyList<ICommittedActionEvaluation> EvaluateBlock(IPreEvaluationBlo

/// <summary>
/// Evaluates all actions in the <see cref="PreEvaluationBlock.Transactions"/> and
/// an optional <see cref="Blockchain.Policies.IBlockPolicy.BlockAction"/>, and returns
/// optional <see cref="Policies.IBlockPolicy.BeginBlockActions"/>,
/// <see cref="Policies.IBlockPolicy.EndBlockActions"/>, and returns
/// a <see cref="Block"/> instance combined with the <see cref="Block.StateRootHash"/>
/// The returned <see cref="Block"/> is signed by the given <paramref name="privateKey"/>.
/// </summary>
Expand Down
18 changes: 14 additions & 4 deletions Libplanet/Blockchain/Policies/BlockPolicy.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using System.Linq;
using Libplanet.Action;
Expand Down Expand Up @@ -34,7 +35,11 @@ public class BlockPolicy : IBlockPolicy
/// description for more detail.
/// </para>
/// </summary>
/// <param name="blockAction">A <see cref="IAction"/> to executed for
/// <param name="beginBlockActions">Array of <see cref="IAction"/> to executed for
/// every <see cref="Block"/>. Set to <see langword="null"/> by default, which results
/// in no additional execution other than those included in <see cref="Transaction"/>s.
/// </param>
/// <param name="endBlockActions">A <see cref="IAction"/> to executed for
/// every <see cref="Block"/>. Set to <see langword="null"/> by default, which results
/// in no additional execution other than those included in <see cref="Transaction"/>s.
/// </param>
Expand Down Expand Up @@ -66,7 +71,8 @@ public class BlockPolicy : IBlockPolicy
/// Goes to <see cref="GetMaxTransactionsPerSignerPerBlock"/>. Set to
/// <see cref="GetMaxTransactionsPerBlock"/> by default.</param>
public BlockPolicy(
IAction? blockAction = null,
ImmutableArray<IAction>? beginBlockActions = null,
ImmutableArray<IAction>? endBlockActions = null,
TimeSpan? blockInterval = null,
Func<BlockChain, Transaction, TxPolicyViolationException?>?
validateNextBlockTx = null,
Expand All @@ -77,7 +83,8 @@ public BlockPolicy(
Func<long, int>? getMaxTransactionsPerBlock = null,
Func<long, int>? getMaxTransactionsPerSignerPerBlock = null)
{
BlockAction = blockAction;
BeginBlockActions = beginBlockActions ?? ImmutableArray<IAction>.Empty;
EndBlockActions = endBlockActions ?? ImmutableArray<IAction>.Empty;
BlockInterval = blockInterval ?? DefaultTargetBlockInterval;
_getMaxTransactionsBytes = getMaxTransactionsBytes ?? (_ => 100L * 1024L);
_getMinTransactionsPerBlock = getMinTransactionsPerBlock ?? (_ => 0);
Expand Down Expand Up @@ -152,7 +159,10 @@ public BlockPolicy(
}

/// <inheritdoc/>
public IAction? BlockAction { get; }
public ImmutableArray<IAction> BeginBlockActions { get; }

/// <inheritdoc/>
public ImmutableArray<IAction> EndBlockActions { get; }

/// <summary>
/// Targeted time interval between two consecutive <see cref="Block"/>s.
Expand Down
12 changes: 9 additions & 3 deletions Libplanet/Blockchain/Policies/IBlockPolicy.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using Libplanet.Action;
using Libplanet.Types.Blocks;
Expand All @@ -21,9 +22,14 @@ namespace Libplanet.Blockchain.Policies
public interface IBlockPolicy
{
/// <summary>
/// An <see cref="IAction"/> to execute and be rendered for every block, if any.
/// </summary>
IAction? BlockAction { get; }
/// An array of <see cref="IAction"/> to execute and be rendered at the beginning
/// for every block, if any.</summary>
ImmutableArray<IAction> BeginBlockActions { get; }

/// <summary>
/// An array of <see cref="IAction"/> to execute and be rendered at the end for every block,
/// if any.</summary>
ImmutableArray<IAction> EndBlockActions { get; }

/// <summary>
/// Checks if a <see cref="Transaction"/> can be included in a yet to be mined
Expand Down
5 changes: 4 additions & 1 deletion Libplanet/Blockchain/Policies/NullBlockPolicy.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#nullable disable
using System.Collections.Generic;
using System.Collections.Immutable;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.Types.Blocks;
Expand All @@ -22,7 +23,9 @@ public NullBlockPolicy(

public ISet<Address> BlockedMiners { get; } = new HashSet<Address>();

public IAction BlockAction => null;
public ImmutableArray<IAction> BeginBlockActions => ImmutableArray<IAction>.Empty;

public ImmutableArray<IAction> EndBlockActions => ImmutableArray<IAction>.Empty;

public int GetMinTransactionsPerBlock(long index) => 0;

Expand Down

0 comments on commit 8baee41

Please sign in to comment.