diff --git a/CHANGES.md b/CHANGES.md index b7c7ef2230c..28b946e5d04 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,15 +11,18 @@ Version DPoS - (Libplanet) Removed `IBlockPolicy.BlockAction` property. [[#3701]] - (Libplanet) Added `IBlockPolicy.BeginBlockActions`. property. [[#3701]] - (Libplanet) Added `IBlockPolicy.EndBlockActions`. property. [[#3701]] - - (Libplanet) `BlockPolicy` constructor requires `beginBlockActions` and - `endBlockActions` parameters instead of the `blockAction` parameter. - [[#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 - `PolicyBlockActionGetter` and changed return type to - `ImmutableArray`. [[#3701]] + `PolicyActionsGetter` and changed return type to + `ImmutableArray`. [[#3701], [#3748]] - (Libplanet.Action) `ActionEvaluator` constructor requires - `policyBeginBlockActionGetter` and `policyEndBlockActionGetter` - parameters instead of the `policyBlockActionGetter` parameter. [[#3701]] + `PolicyActionsRegistry` parameter instead of the + `policyBlockActionGetter` parameter. [[#3701], [#3748]] - (Libplanet.Action) Added `SetValidatorSet` method to `IAccount` interface and its implementations. [[#3730]] - (Libplanet.Explorer) Added `ValidatorPower` field to `VoteType`. [[#3737]] @@ -32,6 +35,8 @@ Version DPoS ### Added APIs + - (Libplanet.Action) Added `PolicyActionsRegistry` class. [[#3748]] + ### Behavioral changes ### Bug fixes @@ -43,6 +48,7 @@ Version DPoS [#3701]: https://github.com/planetarium/libplanet/pull/3701 [#3730]: https://github.com/planetarium/libplanet/pull/3730 [#3737]: https://github.com/planetarium/libplanet/pull/3737 +[#3748]: https://github.com/planetarium/libplanet/pull/3748 Version 4.1.0 diff --git a/Libplanet.Action.Tests/Common/UpdateValueAction.cs b/Libplanet.Action.Tests/Common/UpdateValueAction.cs index ffca5b86017..329733ba216 100644 --- a/Libplanet.Action.Tests/Common/UpdateValueAction.cs +++ b/Libplanet.Action.Tests/Common/UpdateValueAction.cs @@ -6,25 +6,27 @@ namespace Libplanet.Action.Tests.Common { public sealed class UpdateValueAction : IAction { - public static readonly Address ValueAddress = - new Address("0000000000000000000000000000000000000123"); - public UpdateValueAction() { } - public UpdateValueAction(int increment) + public UpdateValueAction(Address address, int increment) { + Address = address; Increment = increment; } + public Address Address { get; set; } + public int Increment { get; set; } public IValue PlainValue => Bencodex.Types.Dictionary.Empty + .Add("address", Address.Bencoded) .Add("value", new Bencodex.Types.Integer(Increment)); public void LoadPlainValue(IValue plainValue) { + Address = new Address(((Dictionary)plainValue)["address"]); Increment = (int)(Bencodex.Types.Integer)((Dictionary)plainValue)["value"]; } @@ -35,12 +37,16 @@ public IWorld Execute(IActionContext ctx) int value = 0; int increment = Increment; - if (account.GetState(ValueAddress) is Integer integer) + if (account.GetState(Address) is Integer integer) { value = (int)integer.Value + increment; } + else + { + value = increment; + } - account = account.SetState(ValueAddress, new Integer(value)); + account = account.SetState(Address, new Integer(value)); return states.SetAccount(ReservedAddresses.LegacyAccount, account); } } diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index a628bf49a9f..1aff7d80842 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -10,7 +10,6 @@ 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; @@ -26,37 +25,29 @@ namespace Libplanet.Action public class ActionEvaluator : IActionEvaluator { private readonly ILogger _logger; - private readonly PolicyBlockActionsGetter _policyBeginBlockActionsGetter; - private readonly PolicyBlockActionsGetter _policyEndBlockActionsGetter; + private readonly PolicyActionsRegistry _policyActionsRegistry; private readonly IStateStore _stateStore; private readonly IActionLoader _actionLoader; /// /// Creates a new . /// - /// A delegator to get policy block actions to - /// evaluate at the beginning for each - /// that gets evaluated. - /// Note the order of the returned list determines the execution order. - /// - /// A delegator to get policy block actions to - /// evaluate at the end for each that gets evaluated. - /// Note the order of the returned list determines the execution order. + /// + /// A containing delegators + /// to get policy actions to evaluate at each situation. /// /// The to use to retrieve /// the states for a provided . /// A implementation using /// action type lookup. public ActionEvaluator( - PolicyBlockActionsGetter policyBeginBlockActionsGetter, - PolicyBlockActionsGetter policyEndBlockActionsGetter, + PolicyActionsRegistry policyActionsRegistry, IStateStore stateStore, IActionLoader actionTypeLoader) { _logger = Log.ForContext() .ForContext("Source", nameof(ActionEvaluator)); - _policyBeginBlockActionsGetter = policyBeginBlockActionsGetter; - _policyEndBlockActionsGetter = policyEndBlockActionsGetter; + _policyActionsRegistry = policyActionsRegistry; _stateStore = stateStore; _actionLoader = actionTypeLoader; } @@ -134,7 +125,8 @@ public IReadOnlyList Evaluate( } var evaluations = ImmutableList.Empty; - if (_policyBeginBlockActionsGetter(block) is { } beginBlockActions && + if (_policyActionsRegistry.BeginBlockActionsGetter(block) is + { } beginBlockActions && beginBlockActions.Length > 0) { evaluations = evaluations.AddRange(EvaluatePolicyBeginBlockActions( @@ -147,7 +139,7 @@ public IReadOnlyList Evaluate( EvaluateBlock(block, previousState).ToImmutableList() ); - if (_policyEndBlockActionsGetter(block) is { } endBlockActions && + if (_policyActionsRegistry.EndBlockActionsGetter(block) is { } endBlockActions && endBlockActions.Length > 0) { previousState = evaluations.Count > 0 @@ -491,27 +483,50 @@ internal IEnumerable EvaluateTx( ITransaction tx, IWorld previousState) { + var evaluations = ImmutableList.Empty; + if (_policyActionsRegistry.BeginTxActionsGetter(blockHeader) is + { } beginTxActions && + beginTxActions.Length > 0) + { + evaluations = evaluations.AddRange( + EvaluatePolicyBeginTxActions(blockHeader, previousState)); + previousState = evaluations.Last().OutputState; + } + ImmutableList actions = ImmutableList.CreateRange(LoadActions(blockHeader.Index, tx)); - return EvaluateActions( + evaluations = evaluations.AddRange(EvaluateActions( blockHeader: blockHeader, tx: tx, previousState: previousState, actions: actions, stateStore: _stateStore, - logger: _logger); + logger: _logger)); + + if (_policyActionsRegistry.EndTxActionsGetter(blockHeader) is + { } endTxActions && + endTxActions.Length > 0) + { + previousState = evaluations.Count > 0 + ? evaluations.Last().OutputState + : previousState; + evaluations = evaluations.AddRange( + EvaluatePolicyEndTxActions(blockHeader, previousState)); + } + + return evaluations; } /// - /// Evaluates the set by the policy when + /// Evaluates the set by the policy when /// this was instantiated for a given /// . /// /// The header of the block to evaluate. /// The states immediately before the evaluation of - /// the held by the instance. + /// the held by the instance. /// The of evaluating - /// the held by the instance + /// the held by the instance /// for the . [Pure] internal ActionEvaluation[] EvaluatePolicyBeginBlockActions( @@ -526,21 +541,21 @@ internal ActionEvaluation[] EvaluatePolicyBeginBlockActions( blockHeader: blockHeader, tx: null, previousState: previousState, - actions: _policyBeginBlockActionsGetter(blockHeader), + actions: _policyActionsRegistry.BeginBlockActionsGetter(blockHeader), stateStore: _stateStore, logger: _logger).ToArray(); } /// - /// Evaluates the set by the policy when + /// Evaluates the set by the policy when /// this was instantiated for a given /// . /// /// The header of the block to evaluate. /// The states immediately before the evaluation of - /// the held by the instance. + /// the held by the instance. /// The of evaluating - /// the held by the instance + /// the held by the instance /// for the . [Pure] internal ActionEvaluation[] EvaluatePolicyEndBlockActions( @@ -555,7 +570,65 @@ internal ActionEvaluation[] EvaluatePolicyEndBlockActions( blockHeader: blockHeader, tx: null, previousState: previousState, - actions: _policyEndBlockActionsGetter(blockHeader), + actions: _policyActionsRegistry.EndBlockActionsGetter(blockHeader), + stateStore: _stateStore, + logger: _logger).ToArray(); + } + + /// + /// Evaluates the set by the policy when + /// this was instantiated for a given + /// . + /// + /// The header of the block to evaluate. + /// The states immediately before the evaluation of + /// the held by the instance. + /// The of evaluating + /// the held by the instance + /// for the . + [Pure] + internal ActionEvaluation[] EvaluatePolicyBeginTxActions( + IPreEvaluationBlockHeader blockHeader, + IWorld previousState) + { + _logger.Information( + $"Evaluating policy begin tx actions for block #{blockHeader.Index} " + + $"{ByteUtil.Hex(blockHeader.PreEvaluationHash.ByteArray)}"); + + return EvaluateActions( + blockHeader: blockHeader, + tx: null, + previousState: previousState, + actions: _policyActionsRegistry.BeginTxActionsGetter(blockHeader), + stateStore: _stateStore, + logger: _logger).ToArray(); + } + + /// + /// Evaluates the set by the policy when + /// this was instantiated for a given + /// . + /// + /// The header of the block to evaluate. + /// The states immediately before the evaluation of + /// the held by the instance. + /// The of evaluating + /// the held by the instance + /// for the . + [Pure] + internal ActionEvaluation[] EvaluatePolicyEndTxActions( + IPreEvaluationBlockHeader blockHeader, + IWorld previousState) + { + _logger.Information( + $"Evaluating policy end tx actions for block #{blockHeader.Index} " + + $"{ByteUtil.Hex(blockHeader.PreEvaluationHash.ByteArray)}"); + + return EvaluateActions( + blockHeader: blockHeader, + tx: null, + previousState: previousState, + actions: _policyActionsRegistry.EndTxActionsGetter(blockHeader), stateStore: _stateStore, logger: _logger).ToArray(); } diff --git a/Libplanet.Action/PolicyBlockActionsGetter.cs b/Libplanet.Action/PolicyActionsGetter.cs similarity index 68% rename from Libplanet.Action/PolicyBlockActionsGetter.cs rename to Libplanet.Action/PolicyActionsGetter.cs index 41dcfef0ca9..b1e5fcf703e 100644 --- a/Libplanet.Action/PolicyBlockActionsGetter.cs +++ b/Libplanet.Action/PolicyActionsGetter.cs @@ -3,7 +3,7 @@ namespace Libplanet.Action { - public delegate ImmutableArray PolicyBlockActionsGetter( + public delegate ImmutableArray PolicyActionsGetter( IPreEvaluationBlockHeader blockHeader ); } diff --git a/Libplanet.Action/PolicyActionsRegistry.cs b/Libplanet.Action/PolicyActionsRegistry.cs new file mode 100644 index 00000000000..000553d85d1 --- /dev/null +++ b/Libplanet.Action/PolicyActionsRegistry.cs @@ -0,0 +1,49 @@ +using Libplanet.Types.Blocks; +using Libplanet.Types.Tx; + +namespace Libplanet.Action +{ + public class PolicyActionsRegistry + { + /// + /// A class containing delegators + /// to get policy actions to evaluate at each situation. + /// + /// A delegator to get policy block actions to + /// evaluate at the beginning for each that gets + /// evaluated. + /// Note the order of the returned list determines the execution order. + /// + /// A delegator to get policy block actions to + /// evaluate at the end for each that gets evaluated. + /// Note the order of the returned list determines the execution order. + /// + /// A delegator to get policy block actions to + /// evaluate at the beginning for each that gets evaluated. + /// Note the order of the returned list determines the execution order. + /// + /// A delegator to get policy block actions to + /// evaluate at the end for each that gets evaluated. + /// Note the order of the returned list determines the execution order. + /// + public PolicyActionsRegistry( + PolicyActionsGetter beginBlockActionsGetter, + PolicyActionsGetter endBlockActionsGetter, + PolicyActionsGetter beginTxActionsGetter, + PolicyActionsGetter endTxActionsGetter) + { + BeginBlockActionsGetter = beginBlockActionsGetter; + EndBlockActionsGetter = endBlockActionsGetter; + BeginTxActionsGetter = beginTxActionsGetter; + EndTxActionsGetter = endTxActionsGetter; + } + + public PolicyActionsGetter BeginBlockActionsGetter { get; } + + public PolicyActionsGetter EndBlockActionsGetter { get; } + + public PolicyActionsGetter BeginTxActionsGetter { get; } + + public PolicyActionsGetter EndTxActionsGetter { get; } + } +} diff --git a/Libplanet.Benchmarks/AppendBlock.cs b/Libplanet.Benchmarks/AppendBlock.cs index 9cec1a71690..a38cbbb25d3 100644 --- a/Libplanet.Benchmarks/AppendBlock.cs +++ b/Libplanet.Benchmarks/AppendBlock.cs @@ -2,7 +2,6 @@ using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Action.Tests.Common; -using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; using Libplanet.Crypto; using Libplanet.Types.Blocks; @@ -30,8 +29,11 @@ public AppendBlock() fx.StateStore, fx.GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); _privateKey = new PrivateKey(); diff --git a/Libplanet.Benchmarks/BlockChain.cs b/Libplanet.Benchmarks/BlockChain.cs index c29de36e58a..5d00adbc784 100644 --- a/Libplanet.Benchmarks/BlockChain.cs +++ b/Libplanet.Benchmarks/BlockChain.cs @@ -3,7 +3,6 @@ using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Action.Tests.Common; -using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; using Libplanet.Crypto; using Libplanet.Tests.Store; @@ -36,8 +35,11 @@ public void SetupChain() _fx.StateStore, _fx.GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: _fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); var key = new PrivateKey(); diff --git a/Libplanet.Benchmarks/ProposeBlock.cs b/Libplanet.Benchmarks/ProposeBlock.cs index b0e441feada..df298f630f0 100644 --- a/Libplanet.Benchmarks/ProposeBlock.cs +++ b/Libplanet.Benchmarks/ProposeBlock.cs @@ -2,7 +2,6 @@ using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Action.Tests.Common; -using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; using Libplanet.Crypto; using Libplanet.Types.Blocks; @@ -29,8 +28,11 @@ public ProposeBlock() fx.StateStore, fx.GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); _privateKey = new PrivateKey(); diff --git a/Libplanet.Explorer.Executable/Program.cs b/Libplanet.Explorer.Executable/Program.cs index 3c6154c97b0..e7fcfc0c0e4 100644 --- a/Libplanet.Explorer.Executable/Program.cs +++ b/Libplanet.Explorer.Executable/Program.cs @@ -200,8 +200,11 @@ If omitted (default) explorer only the local blockchain store.")] options.GetGenesisBlock(policy), blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new SingleActionLoader(typeof(NullAction)))); Startup.PreloadedSingleton = false; @@ -390,6 +393,10 @@ public DumbBlockPolicy(BlockPolicy blockPolicy) public ImmutableArray EndBlockActions => _impl.EndBlockActions; + public ImmutableArray BeginTxActions => _impl.BeginTxActions; + + public ImmutableArray EndTxActions => _impl.EndTxActions; + public int GetMinTransactionsPerBlock(long index) => _impl.GetMinTransactionsPerBlock(index); diff --git a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs index 34baa7bbe01..143b42e47b9 100644 --- a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs +++ b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs @@ -5,7 +5,6 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; -using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -72,8 +71,11 @@ public GeneratedBlockChainFixture( getMaxTransactionsPerBlock: _ => int.MaxValue, getMaxTransactionsBytes: _ => long.MaxValue); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, TypedActionLoader.Create(typeof(SimpleAction).Assembly, typeof(SimpleAction))); Block genesisBlock = BlockChain.ProposeGenesisBlock( diff --git a/Libplanet.Extensions.Cocona.Tests/BlockPolicyParamsTest.cs b/Libplanet.Extensions.Cocona.Tests/BlockPolicyParamsTest.cs index 5ecc1a6900c..c5d9da4561b 100644 --- a/Libplanet.Extensions.Cocona.Tests/BlockPolicyParamsTest.cs +++ b/Libplanet.Extensions.Cocona.Tests/BlockPolicyParamsTest.cs @@ -15,6 +15,8 @@ public void DefaultState() Assert.Null(blockPolicyParams.GetBlockPolicy()); Assert.Empty(blockPolicyParams.GetBeginBlockActions()); Assert.Empty(blockPolicyParams.GetEndBlockActions()); + Assert.Empty(blockPolicyParams.GetBeginTxActions()); + Assert.Empty(blockPolicyParams.GetEndTxActions()); } [Fact] @@ -31,6 +33,10 @@ public void GetBlockPolicy() Assert.IsType(blockPolicy.BeginBlockActions[0]); Assert.Single(blockPolicy.EndBlockActions); Assert.IsType(blockPolicy.EndBlockActions[0]); + Assert.Single(blockPolicy.BeginTxActions); + Assert.IsType(blockPolicy.BeginTxActions[0]); + Assert.Single(blockPolicy.EndTxActions); + Assert.IsType(blockPolicy.EndTxActions[0]); } [Fact] @@ -150,10 +156,36 @@ public void GetEndBlockActions() Assert.IsType(blockActions[0]); } + [Fact] + public void GetBeginTxActions() + { + var blockPolicyParams = new BlockPolicyParams + { + PolicyFactory = $"{GetType().FullName}.{nameof(BlockPolicyFactory)}", + }; + var blockActions = blockPolicyParams.GetBeginTxActions(new[] { GetType().Assembly }); + Assert.Single(blockActions); + Assert.IsType(blockActions[0]); + } + + [Fact] + public void GetEndTxActions() + { + var blockPolicyParams = new BlockPolicyParams + { + PolicyFactory = $"{GetType().FullName}.{nameof(BlockPolicyFactory)}", + }; + var blockActions = blockPolicyParams.GetEndTxActions(new[] { GetType().Assembly }); + Assert.Single(blockActions); + Assert.IsType(blockActions[0]); + } + internal static BlockPolicy BlockPolicyFactory() => new BlockPolicy( beginBlockActions: new IAction[] { new NullAction() }.ToImmutableArray(), - endBlockActions: new IAction[] { new NullAction() }.ToImmutableArray() + endBlockActions: new IAction[] { new NullAction() }.ToImmutableArray(), + beginTxActions: new IAction[] { new NullAction() }.ToImmutableArray(), + endTxActions: new IAction[] { new NullAction() }.ToImmutableArray() ); internal static BlockPolicy BlockPolicyFactoryWithParams(bool param) => diff --git a/Libplanet.Extensions.Cocona/BlockPolicyParams.cs b/Libplanet.Extensions.Cocona/BlockPolicyParams.cs index 40ef8e186b6..0464e8bb9ea 100644 --- a/Libplanet.Extensions.Cocona/BlockPolicyParams.cs +++ b/Libplanet.Extensions.Cocona/BlockPolicyParams.cs @@ -77,6 +77,12 @@ public ImmutableArray GetBeginBlockActions() => public ImmutableArray GetEndBlockActions() => GetEndBlockActions(LoadAssemblies()); + public ImmutableArray GetBeginTxActions() => + GetBeginTxActions(LoadAssemblies()); + + public ImmutableArray GetEndTxActions() => + GetEndTxActions(LoadAssemblies()); + [SuppressMessage( "Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, " + @@ -180,8 +186,8 @@ internal ImmutableArray GetEndBlockActions(Assembly[] assemblies) if (propertyInfo is null) { var message = $"The policy type " - + $"'{policy.GetType().FullName}' does not have a " - + $"'{nameof(IBlockPolicy.EndBlockActions)}' property."; + + $"'{policy.GetType().FullName}' does not have a " + + $"'{nameof(IBlockPolicy.EndBlockActions)}' property."; throw new InvalidOperationException(message); } @@ -189,8 +195,70 @@ internal ImmutableArray GetEndBlockActions(Assembly[] assemblies) if (value is null) { var message = $"The value of property " - + $"'{nameof(IBlockPolicy.EndBlockActions)}' of type " - + $"'{policy.GetType().FullName}' cannot be null."; + + $"'{nameof(IBlockPolicy.EndBlockActions)}' of type " + + $"'{policy.GetType().FullName}' cannot be null."; + throw new InvalidOperationException(message); + } + + return (ImmutableArray)value; + } + + internal ImmutableArray GetBeginTxActions(Assembly[] assemblies) + { + object? policy = GetBlockPolicy(assemblies); + if (policy is null) + { + return ImmutableArray.Empty; + } + + PropertyInfo? propertyInfo = policy + .GetType() + .GetProperty(nameof(IBlockPolicy.BeginTxActions)); + if (propertyInfo is null) + { + var message = $"The policy type " + + $"'{policy.GetType().FullName}' does not have a " + + $"'{nameof(IBlockPolicy.BeginTxActions)}' property."; + throw new InvalidOperationException(message); + } + + var value = propertyInfo.GetValue(policy); + if (value is null) + { + var message = $"The value of property " + + $"'{nameof(IBlockPolicy.BeginTxActions)}' of type " + + $"'{policy.GetType().FullName}' cannot be null."; + throw new InvalidOperationException(message); + } + + return (ImmutableArray)value; + } + + internal ImmutableArray GetEndTxActions(Assembly[] assemblies) + { + object? policy = GetBlockPolicy(assemblies); + if (policy is null) + { + return ImmutableArray.Empty; + } + + PropertyInfo? propertyInfo = policy + .GetType() + .GetProperty(nameof(IBlockPolicy.EndTxActions)); + if (propertyInfo is null) + { + var message = $"The policy type " + + $"'{policy.GetType().FullName}' does not have a " + + $"'{nameof(IBlockPolicy.EndTxActions)}' property."; + throw new InvalidOperationException(message); + } + + var value = propertyInfo.GetValue(policy); + if (value is null) + { + var message = $"The value of property " + + $"'{nameof(IBlockPolicy.EndTxActions)}' of type " + + $"'{policy.GetType().FullName}' cannot be null."; throw new InvalidOperationException(message); } diff --git a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs index bd5f57df337..97d692f25cd 100644 --- a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs +++ b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs @@ -158,9 +158,14 @@ public void GenerateGenesis( var beginBlockActions = blockPolicyParams.GetBeginBlockActions(); var endBlockActions = blockPolicyParams.GetEndBlockActions(); + var beginTxActions = blockPolicyParams.GetBeginTxActions(); + var endTxActions = blockPolicyParams.GetEndTxActions(); var actionEvaluator = new ActionEvaluator( - _ => beginBlockActions, - _ => endBlockActions, + new PolicyActionsRegistry( + _ => beginBlockActions, + _ => endBlockActions, + _ => beginTxActions, + _ => endTxActions), new TrieStateStore(new DefaultKeyValueStore(null)), new SingleActionLoader(typeof(NullAction))); Block genesis = BlockChain.ProposeGenesisBlock( diff --git a/Libplanet.Net.Tests/Consensus/ConsensusReactorTest.cs b/Libplanet.Net.Tests/Consensus/ConsensusReactorTest.cs index e7d5cd0da82..094c6c528a3 100644 --- a/Libplanet.Net.Tests/Consensus/ConsensusReactorTest.cs +++ b/Libplanet.Net.Tests/Consensus/ConsensusReactorTest.cs @@ -69,8 +69,11 @@ public async void StartAsync() stateStore, fx.GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => TestUtils.Policy.BeginBlockActions, - policyEndBlockActionsGetter: _ => TestUtils.Policy.EndBlockActions, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => TestUtils.Policy.BeginBlockActions, + endBlockActionsGetter: _ => TestUtils.Policy.EndBlockActions, + beginTxActionsGetter: _ => TestUtils.Policy.BeginTxActions, + endTxActionsGetter: _ => TestUtils.Policy.EndTxActions), stateStore: stateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); } diff --git a/Libplanet.Net.Tests/SwarmTest.Broadcast.cs b/Libplanet.Net.Tests/SwarmTest.Broadcast.cs index 98c3db41687..21530bdb2ed 100644 --- a/Libplanet.Net.Tests/SwarmTest.Broadcast.cs +++ b/Libplanet.Net.Tests/SwarmTest.Broadcast.cs @@ -470,8 +470,11 @@ public async Task BroadcastTxAsyncMany() fxs[i].StateStore, fxs[i].GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => policy.BeginBlockActions, - policyEndBlockActionsGetter_ => policy.EndBlockActions, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => policy.BeginBlockActions, + endBlockActionsGetter: _ => policy.EndBlockActions, + beginTxActionsGetter: _ => policy.BeginTxActions, + endTxActionsGetter: _ => policy.EndTxActions), stateStore: fxs[i].StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); swarms[i] = await CreateSwarm(blockChains[i]).ConfigureAwait(false); diff --git a/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs b/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs index 2927b6d94cd..a0d1f41eb73 100644 --- a/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs +++ b/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs @@ -87,8 +87,11 @@ public void ReopenStoreAfterDispose() stateStore, Fx.GenesisBlock, new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + policyActionsRegistry: new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: stateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); store.Dispose(); diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index dd26af3787e..8a6e0800b97 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -39,6 +39,18 @@ public class ActionEvaluatorTest private readonly StoreFixture _storeFx; private readonly TxFixture _txFx; + private readonly Address _beginBlockValueAddress = + new Address("0000000000000000000000000000000000000120"); + + private readonly Address _endBlockValueAddress = + new Address("0000000000000000000000000000000000000121"); + + private readonly Address _beginTxValueAddress = + new Address("0000000000000000000000000000000000000122"); + + private readonly Address _endTxValueAddress = + new Address("0000000000000000000000000000000000000123"); + public ActionEvaluatorTest(ITestOutputHelper output) { Log.Logger = _logger = new LoggerConfiguration() @@ -48,17 +60,35 @@ public ActionEvaluatorTest(ITestOutputHelper output) .CreateLogger() .ForContext(); - var beginBlockActions = new IAction[] { new UpdateValueAction(1) }; - var endBlockActions = new IAction[] { new MinerReward(1) }; + var beginBlockActions = new IAction[] + { + new UpdateValueAction(_beginBlockValueAddress, 1), + }; + var endBlockActions = new IAction[] + { + new UpdateValueAction(_endBlockValueAddress, 1), + }; + var beginTxActions = new IAction[] + { + new UpdateValueAction(_beginTxValueAddress, 1), + }; + var endTxActions = new IAction[] + { + new UpdateValueAction(_endTxValueAddress, 1), + }; _output = output; _policy = new BlockPolicy( beginBlockActions: beginBlockActions.ToImmutableArray(), endBlockActions: endBlockActions.ToImmutableArray(), + beginTxActions: beginTxActions.ToImmutableArray(), + endTxActions: endTxActions.ToImmutableArray(), getMaxTransactionsBytes: _ => 50 * 1024); _storeFx = new MemoryStoreFixture( _policy.BeginBlockActions, - _policy.EndBlockActions + _policy.EndBlockActions, + _policy.BeginTxActions, + _policy.EndTxActions ); _txFx = new TxFixture(null); } @@ -93,8 +123,11 @@ public void Idempotent() lastCommit: null), transactions: txs).Propose(); var actionEvaluator = new ActionEvaluator( - _ => ImmutableArray.Empty, - _ => ImmutableArray.Empty, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore, new SingleActionLoader(typeof(RandomAction))); Block stateRootBlock = noStateRootBlock.Sign( @@ -186,6 +219,81 @@ public void Evaluate() Assert.Equal((long)(Integer)state, blockIndex); } + [Fact] + public void EvaluateWithPolicyActions() + { + var store = new MemoryStore(); + var stateStore = new TrieStateStore(new MemoryKeyValueStore()); + var (chain, actionEvaluator) = + TestUtils.MakeBlockChainAndActionEvaluator( + policy: _policy, + store: store, + stateStore: stateStore, + actionLoader: new SingleActionLoader(typeof(DumbAction))); + + Assert.Equal( + (Integer)1, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_beginBlockValueAddress)); + Assert.Equal( + (Integer)1, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_endBlockValueAddress)); + Assert.Equal( + (Integer)chain.Genesis.Transactions.Count, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_beginTxValueAddress)); + Assert.Equal( + (Integer)chain.Genesis.Transactions.Count, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_endTxValueAddress)); + + (_, Transaction[] txs) = MakeFixturesForAppendTests(); + var block = chain.ProposeBlock( + GenesisProposer, txs.ToImmutableList(), CreateBlockCommit(chain.Tip)); + var evaluations = actionEvaluator.Evaluate( + block, chain.Store.GetStateRootHash(chain.Tip.Hash)).ToArray(); + + // BeginBlockAction + (BeginTxAction + #Action + EndTxAction) * #Tx + EndBlockAction + Assert.Equal( + 2 + txs.Length * 2 + txs.Aggregate(0, (sum, tx) => sum + tx.Actions.Count), + evaluations.Length); + + chain.Append(block, CreateBlockCommit(block)); + Assert.Equal( + (Integer)2, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_beginBlockValueAddress)); + Assert.Equal( + (Integer)2, + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_endBlockValueAddress)); + Assert.Equal( + (Integer)(chain.Genesis.Transactions.Count + txs.Length), + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_beginTxValueAddress)); + Assert.Equal( + (Integer)(chain.Genesis.Transactions.Count + txs.Length), + chain + .GetWorldState() + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetState(_endTxValueAddress)); + } + [Fact] public void EvaluateWithException() { @@ -297,8 +405,11 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu Block genesis = ProposeGenesisBlock(TestUtils.GenesisProposer); var actionEvaluator = new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: new TrieStateStore(new MemoryKeyValueStore()), actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); @@ -580,8 +691,11 @@ public void EvaluateTx() lastCommit: null), transactions: txs).Propose(); var actionEvaluator = new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: new TrieStateStore(new MemoryKeyValueStore()), actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); @@ -682,8 +796,11 @@ public void EvaluateTxResultThrowingException() var txs = new Transaction[] { tx }; var hash = new BlockHash(GetRandomBytes(BlockHash.Size)); var actionEvaluator = new ActionEvaluator( - policyBeginBlockActionsGetter: _ => ImmutableArray.Empty, - policyEndBlockActionsGetter: _ => ImmutableArray.Empty, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => ImmutableArray.Empty, + endBlockActionsGetter: _ => ImmutableArray.Empty, + beginTxActionsGetter: _ => ImmutableArray.Empty, + endTxActionsGetter: _ => ImmutableArray.Empty), stateStore: new TrieStateStore(new MemoryKeyValueStore()), actionTypeLoader: new SingleActionLoader(typeof(ThrowException)) ); @@ -855,10 +972,10 @@ public void EvaluatePolicyBeginBlockActions() evaluations.Select(item => item.Action).ToImmutableArray()); Assert.Single(evaluations); Assert.Equal( - (Integer)0, + (Integer)1, (Integer)evaluations[0].OutputState .GetAccount(ReservedAddresses.LegacyAccount) - .GetState(UpdateValueAction.ValueAddress)); + .GetState(_beginBlockValueAddress)); Assert.True(evaluations[0].InputContext.BlockAction); previousState = evaluations[0].OutputState; @@ -871,10 +988,10 @@ public void EvaluatePolicyBeginBlockActions() evaluations.Select(item => item.Action).ToImmutableArray()); Assert.Single(evaluations); Assert.Equal( - (Integer)1, + (Integer)2, (Integer)evaluations[0].OutputState .GetAccount(ReservedAddresses.LegacyAccount) - .GetState(UpdateValueAction.ValueAddress)); + .GetState(_beginBlockValueAddress)); Assert.True(evaluations[0].InputContext.BlockAction); } @@ -905,7 +1022,8 @@ public void EvaluatePolicyEndBlockActions() Assert.Equal( (Integer)1, (Integer)evaluations[0].OutputState - .GetAccount(ReservedAddresses.LegacyAccount).GetState(genesis.Miner)); + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(_endBlockValueAddress)); Assert.True(evaluations[0].InputContext.BlockAction); previousState = evaluations[0].OutputState; @@ -920,23 +1038,57 @@ public void EvaluatePolicyEndBlockActions() Assert.Equal( (Integer)2, (Integer)evaluations[0].OutputState - .GetAccount(ReservedAddresses.LegacyAccount).GetState(block.Miner)); + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(_endBlockValueAddress)); Assert.True(evaluations[0].InputContext.BlockAction); + } - chain.Append(block, CreateBlockCommit(block), render: true); - previousState = actionEvaluator.PrepareInitialDelta(genesis.StateRootHash); - var txEvaluations = actionEvaluator.EvaluateBlock( - block, - previousState).ToList(); - previousState = txEvaluations.Last().OutputState; - evaluations = actionEvaluator.EvaluatePolicyEndBlockActions( + [Fact] + public void EvaluatePolicyBeginTxActions() + { + var (chain, actionEvaluator) = MakeBlockChainAndActionEvaluator( + policy: _policy, + store: _storeFx.Store, + stateStore: _storeFx.StateStore, + actionLoader: new SingleActionLoader(typeof(DumbAction)), + genesisBlock: _storeFx.GenesisBlock, + privateKey: GenesisProposer); + (_, Transaction[] txs) = MakeFixturesForAppendTests(); + var genesis = chain.Genesis; + var block = chain.ProposeBlock( + GenesisProposer, txs.ToImmutableList(), CreateBlockCommit(chain.Tip)); + + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); + var evaluations = actionEvaluator.EvaluatePolicyBeginTxActions( + genesis, + previousState); + + Assert.Equal( + chain.Policy.BeginTxActions, + evaluations.Select(item => item.Action).ToImmutableArray()); + Assert.Single(evaluations); + Assert.Equal( + (Integer)1, + (Integer)evaluations[0].OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(_beginTxValueAddress)); + Assert.True(evaluations[0].InputContext.BlockAction); + + previousState = evaluations[0].OutputState; + evaluations = actionEvaluator.EvaluatePolicyBeginTxActions( block, previousState); + Assert.Equal( + chain.Policy.BeginTxActions, + evaluations.Select(item => item.Action).ToImmutableArray()); + Assert.Single(evaluations); Assert.Equal( (Integer)2, (Integer)evaluations[0].OutputState - .GetAccount(ReservedAddresses.LegacyAccount).GetState(block.Miner)); + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(_beginTxValueAddress)); + Assert.True(evaluations[0].InputContext.BlockAction); } [Theory] @@ -1089,8 +1241,8 @@ public void TotalUpdatedFungibleAssets() block, new World(chain.GetWorldState(block.PreviousHash))); - // Does not include policy block action - Assert.Equal(3, evals.Count()); + // Does not include policy block action, but containing policy tx actions + Assert.Equal(3 + block.Transactions.Count * 2, evals.Count()); var latest = evals.Last().OutputState; // Only addresses[0] and addresses[1] succeeded in minting diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 87ab3110a9c..51b3818a09b 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -22,7 +22,6 @@ using Libplanet.Types.Tx; using Serilog; using Xunit; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; namespace Libplanet.Tests.Blockchain { @@ -463,8 +462,11 @@ TxPolicyViolationException IsSignerValid( fx.StateStore, fx.GenesisBlock, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); @@ -594,8 +596,11 @@ public void AppendValidatesBlock() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); Assert.Throws( @@ -770,8 +775,11 @@ public void DoesNotMigrateStateWithoutAction() policy.EndBlockActions); var renderer = new ValidatingActionRenderer(); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs index 06954c9dd95..27738668ffc 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs @@ -141,8 +141,11 @@ public void CanProposeInvalidGenesisBlock() { var policy = new BlockPolicy(); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), fx.StateStore, new SingleActionLoader(typeof(DumbAction))); var genesis = BlockChain.ProposeGenesisBlock( @@ -182,8 +185,11 @@ public void CanProposeInvalidBlock() fx.StateStore, fx.GenesisBlock, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); var txs = new[] @@ -407,8 +413,11 @@ TxPolicyViolationException IsSignerValid( fx.StateStore, fx.GenesisBlock, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); @@ -528,8 +537,11 @@ public void ProposeBlockWithBlockAction() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index bf5910cd633..23a4543e0ac 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -179,8 +179,11 @@ public void ValidateNextBlockInvalidStateRootHash() var stateStore = new TrieStateStore(stateKeyValueStore); IStore store = new MemoryStore(); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction))); var genesisBlock = TestUtils.ProposeGenesisBlock( @@ -220,7 +223,7 @@ public void ValidateNextBlockInvalidStateRootHash() var policyWithBlockAction = new BlockPolicy( beginActions, endActions, - policy.BlockInterval + blockInterval: policy.BlockInterval ); var blockChainStates = new BlockChainStates(store, stateStore); var chain2 = new BlockChain( @@ -231,8 +234,11 @@ public void ValidateNextBlockInvalidStateRootHash() genesisBlock, blockChainStates, new ActionEvaluator( - _ => policyWithBlockAction.BeginBlockActions, - _ => policyWithBlockAction.EndBlockActions, + new PolicyActionsRegistry( + _ => policyWithBlockAction.BeginBlockActions, + _ => policyWithBlockAction.EndBlockActions, + _ => policyWithBlockAction.BeginTxActions, + _ => policyWithBlockAction.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction)))); Assert.Throws(() => diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index e4827c842d7..45c31f395cb 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -66,8 +66,11 @@ public BlockChainTest(ITestOutputHelper output) _fx.StateStore, _fx.GenesisBlock, new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore: _fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction))), renderers: new[] { new LoggedActionRenderer(_renderer, Log.Logger) } @@ -152,8 +155,11 @@ public void CanonicalId() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); @@ -199,8 +205,11 @@ public void ProcessActions() var actionLoader = TypedActionLoader.Create( typeof(BaseAction).Assembly, typeof(BaseAction)); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, actionLoader); var nonce = 0; @@ -635,8 +644,11 @@ public void ForkShouldSkipExecuteAndRenderGenesis() using (var stateStore = new TrieStateStore(new MemoryKeyValueStore())) { var actionEvaluator = new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction))); var privateKey = new PrivateKey(); @@ -669,8 +681,11 @@ public void ForkShouldSkipExecuteAndRenderGenesis() stateStore, genesis, new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore: stateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction))), renderers: new[] { renderer } @@ -1065,8 +1080,11 @@ public void ReorgIsUnableToHeterogenousChain(bool render) using (var fx2 = new MemoryStoreFixture(beginActions, endActions)) { var actionEvaluator = new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore: fx2.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); Block genesis2 = ProposeGenesisBlock( @@ -1123,8 +1141,11 @@ public void GetStatesOnCreatingBlockChain() IStore store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction))); Block genesisWithTx = ProposeGenesisBlock( @@ -1167,8 +1188,11 @@ public void GetStateOnlyDrillsDownUntilRequestedAddressesAreFound() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); @@ -1227,8 +1251,11 @@ public void GetStateReturnsEarlyForNonexistentAccount() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); @@ -1329,8 +1356,11 @@ public void GetStateReturnsLatestStatesWhenMultipleAddresses() _fx.GenesisBlock, blockChainStates, new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); @@ -1430,8 +1460,11 @@ public void FindBranchPoint() emptyFx.StateStore, emptyFx.GenesisBlock, new ActionEvaluator( - _ => _blockChain.Policy.BeginBlockActions, - _ => _blockChain.Policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _blockChain.Policy.BeginBlockActions, + _ => _blockChain.Policy.EndBlockActions, + _ => _blockChain.Policy.BeginTxActions, + _ => _blockChain.Policy.EndTxActions), stateStore: emptyFx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); var fork = BlockChain.Create( @@ -1441,8 +1474,11 @@ public void FindBranchPoint() forkFx.StateStore, forkFx.GenesisBlock, new ActionEvaluator( - _ => _blockChain.Policy.BeginBlockActions, - _ => _blockChain.Policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _blockChain.Policy.BeginBlockActions, + _ => _blockChain.Policy.EndBlockActions, + _ => _blockChain.Policy.BeginTxActions, + _ => _blockChain.Policy.EndTxActions), stateStore: forkFx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); fork.Append(b1, CreateBlockCommit(b1)); @@ -1798,8 +1834,11 @@ internal static (Address, Address[] Addresses, BlockChain Chain) Guid chainId = Guid.NewGuid(); var chainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BeginBlockActions, - _ => blockPolicy.EndBlockActions, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore: stateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); Block genesisBlock = ProposeGenesisBlock( @@ -2039,8 +2078,11 @@ private void CreateWithGenesisBlock() var blockChainStates = new BlockChainStates( storeFixture.Store, storeFixture.StateStore); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), storeFixture.StateStore, new SingleActionLoader(typeof(DumbAction))); BlockChain blockChain = BlockChain.Create( @@ -2087,8 +2129,11 @@ private void ConstructWithUnexpectedGenesisBlock() var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction))); var genesisBlockA = BlockChain.ProposeGenesisBlock(actionEvaluator); @@ -2171,8 +2216,11 @@ private void CheckIfTxPolicyExceptionHasInnerException() null, List.Empty); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new SingleActionLoader(typeof(DumbAction))); var genesisWithTx = ProposeGenesisBlock( @@ -2237,8 +2285,11 @@ private void ValidateNextBlockCommitOnValidatorSetChange() .ToImmutableList(); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), storeFixture.StateStore, new SingleActionLoader(typeof(SetValidator))); Block genesis = BlockChain.ProposeGenesisBlock( diff --git a/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs b/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs index 1b8f58abd72..354cadc5b08 100644 --- a/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs +++ b/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs @@ -44,8 +44,11 @@ public BlockPolicyTest(ITestOutputHelper output) _fx.StateStore, _fx.GenesisBlock, new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore: _fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); } diff --git a/Libplanet.Tests/Blockchain/Policies/StagePolicyTest.cs b/Libplanet.Tests/Blockchain/Policies/StagePolicyTest.cs index 3e114184bce..de53121643e 100644 --- a/Libplanet.Tests/Blockchain/Policies/StagePolicyTest.cs +++ b/Libplanet.Tests/Blockchain/Policies/StagePolicyTest.cs @@ -31,8 +31,11 @@ protected StagePolicyTest() _fx.StateStore, _fx.GenesisBlock, new ActionEvaluator( - _ => _policy.BeginBlockActions, - _ => _policy.EndBlockActions, + new PolicyActionsRegistry( + _ => _policy.BeginBlockActions, + _ => _policy.EndBlockActions, + _ => _policy.BeginTxActions, + _ => _policy.EndTxActions), stateStore: _fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(DumbAction)))); _key = new PrivateKey(); diff --git a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs index 4fc84113602..f4192afc8d0 100644 --- a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs +++ b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs @@ -50,8 +50,11 @@ public void Evaluate() using (var fx = new MemoryStoreFixture()) { var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), fx.StateStore, new SingleActionLoader(typeof(Arithmetic))); Block genesis = preEvalGenesis.Sign( @@ -130,8 +133,11 @@ public void DetermineStateRootHash() using (var fx = new MemoryStoreFixture()) { var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + policyActionsRegistry: new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore: fx.StateStore, actionTypeLoader: new SingleActionLoader(typeof(Arithmetic))); HashDigest genesisStateRootHash = diff --git a/Libplanet.Tests/Fixtures/IntegerSet.cs b/Libplanet.Tests/Fixtures/IntegerSet.cs index 9bc57b87d6d..a656e011a2d 100644 --- a/Libplanet.Tests/Fixtures/IntegerSet.cs +++ b/Libplanet.Tests/Fixtures/IntegerSet.cs @@ -71,8 +71,11 @@ public IntegerSet( KVStore = new MemoryKeyValueStore(); StateStore = new TrieStateStore(KVStore); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), StateStore, new SingleActionLoader(typeof(Arithmetic))); Genesis = TestUtils.ProposeGenesisBlock( diff --git a/Libplanet.Tests/Store/MemoryStoreFixture.cs b/Libplanet.Tests/Store/MemoryStoreFixture.cs index 914dd4dc9b9..89380e16fb2 100644 --- a/Libplanet.Tests/Store/MemoryStoreFixture.cs +++ b/Libplanet.Tests/Store/MemoryStoreFixture.cs @@ -9,8 +9,10 @@ public class MemoryStoreFixture : StoreFixture { public MemoryStoreFixture( ImmutableArray? beginBlockActions = null, - ImmutableArray? endBlockActions = null) - : base(beginBlockActions, endBlockActions) + ImmutableArray? endBlockActions = null, + ImmutableArray? beginTxActions = null, + ImmutableArray? endTxActions = null) + : base(beginBlockActions, endBlockActions, beginTxActions, endTxActions) { Store = new MemoryStore(); StateStore = new TrieStateStore(new MemoryKeyValueStore()); diff --git a/Libplanet.Tests/Store/StoreFixture.cs b/Libplanet.Tests/Store/StoreFixture.cs index 7a12a0471b2..6b67cda1a59 100644 --- a/Libplanet.Tests/Store/StoreFixture.cs +++ b/Libplanet.Tests/Store/StoreFixture.cs @@ -20,7 +20,9 @@ public abstract class StoreFixture : IDisposable { protected StoreFixture( ImmutableArray? beginBlockActions = null, - ImmutableArray? endBlockActions = null + ImmutableArray? endBlockActions = null, + ImmutableArray? beginTxActions = null, + ImmutableArray? endTxActions = null ) { Path = null; @@ -105,8 +107,11 @@ protected StoreFixture( proposer: Proposer.PublicKey, validatorSet: TestUtils.ValidatorSet); var actionEvaluator = new ActionEvaluator( - _ => beginBlockActions ?? ImmutableArray.Empty, - _ => endBlockActions ?? ImmutableArray.Empty, + new PolicyActionsRegistry( + _ => beginBlockActions ?? ImmutableArray.Empty, + _ => endBlockActions ?? ImmutableArray.Empty, + _ => beginTxActions ?? ImmutableArray.Empty, + _ => endTxActions ?? ImmutableArray.Empty), stateStore, new SingleActionLoader(typeof(DumbAction))); GenesisBlock = preEval.Sign( diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index ae1f2e9cfa3..3ba4dee3fa5 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -1008,8 +1008,11 @@ public void Copy() var policy = new NullBlockPolicy(); var preEval = ProposeGenesis(proposer: GenesisProposer.PublicKey); var actionEvaluator = new ActionEvaluator( - _ => policy.BeginBlockActions, - _ => policy.EndBlockActions, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), fx.StateStore, new SingleActionLoader(typeof(DumbAction))); var genesis = preEval.Sign( diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index 63e0dcb2ca1..3f32ba3ec49 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -608,10 +608,13 @@ public static (BlockChain BlockChain, ActionEvaluator ActionEvaluator) var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( + new PolicyActionsRegistry( _ => policy.BeginBlockActions, _ => policy.EndBlockActions, - stateStore: stateStore, - actionTypeLoader: actionLoader); + _ => policy.BeginTxActions, + _ => policy.EndTxActions), + stateStore: stateStore, + actionTypeLoader: actionLoader); if (genesisBlock is null) { diff --git a/Libplanet/Blockchain/Policies/BlockPolicy.cs b/Libplanet/Blockchain/Policies/BlockPolicy.cs index 9a316912949..2537b8e305c 100644 --- a/Libplanet/Blockchain/Policies/BlockPolicy.cs +++ b/Libplanet/Blockchain/Policies/BlockPolicy.cs @@ -35,12 +35,24 @@ public class BlockPolicy : IBlockPolicy /// description for more detail. /// /// - /// Array of to executed for - /// every . Set to by default, which results + /// + /// Array of to executed for the beginning of every + /// . Set to by default, which results /// in no additional execution other than those included in s. /// - /// A to executed for - /// every . Set to by default, which results + /// + /// Array of to executed for the end of every + /// . Set to by default, which results + /// in no additional execution other than those included in s. + /// + /// + /// Array of to executed for the beginning of every + /// . Set to by default, which results + /// in no additional execution other than those included in s. + /// + /// + /// Array of to executed for the end of every + /// . Set to by default, which results /// in no additional execution other than those included in s. /// /// Goes to . @@ -73,6 +85,8 @@ public class BlockPolicy : IBlockPolicy public BlockPolicy( ImmutableArray? beginBlockActions = null, ImmutableArray? endBlockActions = null, + ImmutableArray? beginTxActions = null, + ImmutableArray? endTxActions = null, TimeSpan? blockInterval = null, Func? validateNextBlockTx = null, @@ -85,6 +99,8 @@ public BlockPolicy( { BeginBlockActions = beginBlockActions ?? ImmutableArray.Empty; EndBlockActions = endBlockActions ?? ImmutableArray.Empty; + BeginTxActions = beginTxActions ?? ImmutableArray.Empty; + EndTxActions = endTxActions ?? ImmutableArray.Empty; BlockInterval = blockInterval ?? DefaultTargetBlockInterval; _getMaxTransactionsBytes = getMaxTransactionsBytes ?? (_ => 100L * 1024L); _getMinTransactionsPerBlock = getMinTransactionsPerBlock ?? (_ => 0); @@ -164,6 +180,12 @@ public BlockPolicy( /// public ImmutableArray EndBlockActions { get; } + /// + public ImmutableArray BeginTxActions { get; } + + /// + public ImmutableArray EndTxActions { get; } + /// /// Targeted time interval between two consecutive s. /// diff --git a/Libplanet/Blockchain/Policies/IBlockPolicy.cs b/Libplanet/Blockchain/Policies/IBlockPolicy.cs index 36f95615b97..673167b5c8a 100644 --- a/Libplanet/Blockchain/Policies/IBlockPolicy.cs +++ b/Libplanet/Blockchain/Policies/IBlockPolicy.cs @@ -27,10 +27,20 @@ public interface IBlockPolicy ImmutableArray BeginBlockActions { get; } /// - /// An array of to execute and be rendered at the end for every block, - /// if any. + /// An array of to execute and be rendered at the end + /// for every block, if any. ImmutableArray EndBlockActions { get; } + /// + /// An array of to execute and be rendered at the beginning + /// for every transaction, if any. + ImmutableArray BeginTxActions { get; } + + /// + /// An array of to execute and be rendered at the end + /// for every transaction, if any. + ImmutableArray EndTxActions { get; } + /// /// Checks if a can be included in a yet to be mined /// that can be appended to the given . diff --git a/Libplanet/Blockchain/Policies/NullBlockPolicy.cs b/Libplanet/Blockchain/Policies/NullBlockPolicy.cs index d8509509ea2..08ef9f59008 100644 --- a/Libplanet/Blockchain/Policies/NullBlockPolicy.cs +++ b/Libplanet/Blockchain/Policies/NullBlockPolicy.cs @@ -27,6 +27,10 @@ public NullBlockPolicy( public ImmutableArray EndBlockActions => ImmutableArray.Empty; + public ImmutableArray BeginTxActions => ImmutableArray.Empty; + + public ImmutableArray EndTxActions => ImmutableArray.Empty; + public int GetMinTransactionsPerBlock(long index) => 0; public int GetMaxTransactionsPerBlock(long index) => int.MaxValue;