From e813b55e66c57a523b0a5266f79f5f7032da105b Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Mon, 18 Sep 2023 10:30:15 +0900 Subject: [PATCH 01/31] Introduce IActionRenderContext --- Libplanet.Action/ActionRenderContext.cs | 82 ++++++++++++++++++++++ Libplanet.Action/IActionRenderContext.cs | 86 ++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 Libplanet.Action/ActionRenderContext.cs create mode 100644 Libplanet.Action/IActionRenderContext.cs diff --git a/Libplanet.Action/ActionRenderContext.cs b/Libplanet.Action/ActionRenderContext.cs new file mode 100644 index 00000000000..83a1ad374c8 --- /dev/null +++ b/Libplanet.Action/ActionRenderContext.cs @@ -0,0 +1,82 @@ +using System.Diagnostics.Contracts; +using System.Security.Cryptography; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Types.Tx; + +namespace Libplanet.Action +{ + public class ActionRenderContext : IActionRenderContext + { + public ActionRenderContext(IActionContext context) + : this( + signer: context.Signer, + txId: context.TxId, + miner: context.Miner, + blockIndex: context.BlockIndex, + blockProtocolVersion: context.BlockProtocolVersion, + rehearsal: context.Rehearsal, + previousState: context.PreviousState.Trie.Hash, + random: context.GetUnconsumedContext().Random, + blockAction: context.BlockAction) + { + } + + public ActionRenderContext( + Address signer, + TxId? txId, + Address miner, + long blockIndex, + int blockProtocolVersion, + bool rehearsal, + HashDigest previousState, + IRandom random, + bool blockAction) + { + Signer = signer; + TxId = txId; + Miner = miner; + BlockIndex = blockIndex; + BlockProtocolVersion = blockProtocolVersion; + Rehearsal = rehearsal; + PreviousState = previousState; + Random = random; + BlockAction = blockAction; + } + + /// + [Pure] + public Address Signer { get; } + + /// + [Pure] + public TxId? TxId { get; } + + /// + [Pure] + public Address Miner { get; } + + /// + [Pure] + public long BlockIndex { get; } + + /// + [Pure] + public int BlockProtocolVersion { get; } + + /// + [Pure] + public bool Rehearsal { get; } + + /// + [Pure] + public HashDigest PreviousState { get; } + + /// + public IRandom Random { get; } + + /// + [Pure] + public bool BlockAction { get; } + } +} diff --git a/Libplanet.Action/IActionRenderContext.cs b/Libplanet.Action/IActionRenderContext.cs new file mode 100644 index 00000000000..42a32aaf1c0 --- /dev/null +++ b/Libplanet.Action/IActionRenderContext.cs @@ -0,0 +1,86 @@ +using System.Diagnostics.Contracts; +using System.Security.Cryptography; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Types.Blocks; +using Libplanet.Types.Tx; + +namespace Libplanet.Action +{ + /// + /// Contextual data determined by a transaction and a block for rendering. + /// + public interface IActionRenderContext + { + /// + /// The of the that contains + /// the to be executed. If the is + /// not part of a , e.g. , + /// this is set to instead. + /// + [Pure] + Address Signer { get; } + + /// + /// The of the that contains + /// the . If the is not part of + /// a , e.g. , + /// this is set to . + /// + [Pure] + TxId? TxId { get; } + + /// + /// The of the that contains + /// the . + /// + [Pure] + Address Miner { get; } + + /// + /// The of the that contains + /// the . + /// + [Pure] + long BlockIndex { get; } + + /// + /// The of the that contains + /// the . + /// + [Pure] + int BlockProtocolVersion { get; } + + /// + /// Whether an is being executed during + /// “rehearsal mode”, that there is nothing + /// in . + /// + [Pure] + bool Rehearsal { get; } + + /// + /// The state root hash of the previous state. + /// + [Pure] + HashDigest PreviousState { get; } + + /// + /// An initialized pseudorandom number generator. Its seed (state) + /// is determined by a block and a transaction, which is + /// deterministic so that every node can replay the same action and + /// then reproduce the same result, while neither a single block miner + /// nor a single transaction signer can predict the result and cheat. + /// + /// A random object that shares interface mostly equivalent + /// to . + IRandom Random { get; } + + /// + /// Whether this action is executed as a block action. + /// if it belongs to a transaction. + /// + [Pure] + bool BlockAction { get; } + } +} From faa366128bfb991d472a7bcabb95fd326cd2df2c Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Mon, 18 Sep 2023 10:47:47 +0900 Subject: [PATCH 02/31] Use IActionRenderContext --- Libplanet.Action/ActionRenderContext.cs | 2 +- .../Renderers/AnonymousActionRendererTest.cs | 10 ++++----- .../Renderers/LoggedActionRendererTest.cs | 11 +++++----- Libplanet/Blockchain/BlockChain.Swap.cs | 4 ++-- .../Renderers/AnonymousActionRenderer.cs | 21 +++++++++++-------- .../Renderers/AtomicActionRenderer.cs | 19 +++++++++-------- .../Debug/RecordingActionRenderer.cs | 4 ++-- .../Renderers/Debug/RenderRecord.cs | 8 +++---- .../Debug/ValidatingActionRenderer.cs | 10 ++++----- .../Blockchain/Renderers/IActionRenderer.cs | 13 ++++++------ .../Renderers/LoggedActionRenderer.cs | 12 +++++------ 11 files changed, 56 insertions(+), 58 deletions(-) diff --git a/Libplanet.Action/ActionRenderContext.cs b/Libplanet.Action/ActionRenderContext.cs index 83a1ad374c8..8857356f2c1 100644 --- a/Libplanet.Action/ActionRenderContext.cs +++ b/Libplanet.Action/ActionRenderContext.cs @@ -17,7 +17,7 @@ public ActionRenderContext(IActionContext context) blockProtocolVersion: context.BlockProtocolVersion, rehearsal: context.Rehearsal, previousState: context.PreviousState.Trie.Hash, - random: context.GetUnconsumedContext().Random, + random: context.Random, blockAction: context.BlockAction) { } diff --git a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs index 4dc584e9ce5..22beb546cd9 100644 --- a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs @@ -16,8 +16,8 @@ public class AnonymousActionRendererTest private static IAccount _account = new Account(MockAccountState.Empty); - private static IActionContext _actionContext = - new ActionContext( + private static IActionRenderContext _actionContext = + new ActionRenderContext(new ActionContext( default, default, default, @@ -25,7 +25,7 @@ public class AnonymousActionRendererTest default, _account, default, - 0); + 0)); private static Exception _exception = new Exception(); @@ -41,7 +41,7 @@ public class AnonymousActionRendererTest [Fact] public void ActionRenderer() { - (IValue, IActionContext, IAccount)? record = null; + (IValue, IActionRenderContext, IAccount)? record = null; var renderer = new AnonymousActionRenderer { ActionRenderer = (action, context, nextStates) => @@ -63,7 +63,7 @@ record = (action, context, nextStates), [Fact] public void ActionErrorRenderer() { - (IValue, IActionContext, Exception)? record = null; + (IValue, IActionRenderContext, Exception)? record = null; var renderer = new AnonymousActionRenderer { ActionErrorRenderer = (action, context, exception) => diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index a6a20d8fa03..1c9d4e66db2 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -68,8 +68,8 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) { bool called = false; LogEvent firstLog = null; - IActionContext actionContext = - new ActionContext( + IActionRenderContext actionContext = + new ActionRenderContext(new ActionContext( default, default, default, @@ -78,13 +78,12 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) _account, default, 0, - rehearsal - ); + rehearsal)); Exception actionError = new Exception(); IActionRenderer actionRenderer; if (error) { - Action render = (action, cxt, e) => + Action render = (action, cxt, e) => { LogEvent[] logs = LogEvents.ToArray(); Assert.Single(logs); @@ -105,7 +104,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) } else { - Action render = (action, cxt, next) => + Action render = (action, cxt, next) => { LogEvent[] logs = LogEvents.ToArray(); Assert.Single(logs); diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index fbfc30203b1..7525fc1d81e 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -232,14 +232,14 @@ internal long RenderActions( { renderer.RenderAction( evaluation.Action, - evaluation.InputContext.GetUnconsumedContext(), + new ActionRenderContext(evaluation.InputContext.GetUnconsumedContext()), evaluation.OutputState); } else { renderer.RenderActionError( evaluation.Action, - evaluation.InputContext.GetUnconsumedContext(), + new ActionRenderContext(evaluation.InputContext.GetUnconsumedContext()), evaluation.Exception); } diff --git a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs index 12cdad41dfc..366b26fc35d 100644 --- a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs @@ -29,15 +29,15 @@ public sealed class AnonymousActionRenderer : AnonymousRenderer, IActionRenderer { /// /// A callback function to be invoked together with - /// . + /// . /// - public Action? ActionRenderer { get; set; } + public Action? ActionRenderer { get; set; } /// /// A callback function to be invoked together with - /// . + /// . /// - public Action? ActionErrorRenderer { get; set; } + public Action? ActionErrorRenderer { get; set; } /// /// A callback function to be invoked together with @@ -46,18 +46,21 @@ public sealed class AnonymousActionRenderer : AnonymousRenderer, IActionRenderer public Action? BlockEndRenderer { get; set; } /// + /// cref="IActionRenderer.RenderAction(IValue, IActionRenderContext, IAccount)"/> public void RenderAction( IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates ) => ActionRenderer?.Invoke(action, context, nextStates); /// - public void RenderActionError(IValue action, IActionContext context, Exception exception) - => ActionErrorRenderer?.Invoke(action, context, exception); + /// cref="IActionRenderer.RenderActionError(IValue, IActionRenderContext, Exception)"/> + public void RenderActionError( + IValue action, + IActionRenderContext context, + Exception exception) => + ActionErrorRenderer?.Invoke(action, context, exception); /// public void RenderBlockEnd(Block oldTip, Block newTip) => diff --git a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs index 1073452edec..0532abb0b5d 100644 --- a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs @@ -20,7 +20,7 @@ namespace Libplanet.Blockchain.Renderers /// public sealed class AtomicActionRenderer : IActionRenderer { - private readonly List<(IValue, IActionContext, IAccount)> _eventBuffer; + private readonly List<(IValue, IActionRenderContext, IAccount)> _eventBuffer; private TxId? _lastTxId; private bool _errored; @@ -35,7 +35,7 @@ public AtomicActionRenderer(IActionRenderer actionRenderer) { ActionRenderer = actionRenderer; _lastTxId = null; - _eventBuffer = new List<(IValue, IActionContext, IAccount)>(); + _eventBuffer = new List<(IValue, IActionRenderContext, IAccount)>(); _errored = false; } @@ -58,11 +58,10 @@ public void RenderBlockEnd(Block oldTip, Block newTip) ActionRenderer.RenderBlockEnd(oldTip, newTip); } - /// + /// public void RenderAction( IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates ) { @@ -81,9 +80,11 @@ IAccount nextStates } } - /// - public void RenderActionError(IValue action, IActionContext context, Exception exception) + /// + public void RenderActionError( + IValue action, + IActionRenderContext context, + Exception exception) { if (!context.TxId.Equals(_lastTxId)) { @@ -102,7 +103,7 @@ public void RenderActionError(IValue action, IActionContext context, Exception e private void FlushBuffer( TxId? newTxId, - Action render + Action render ) { if (!_errored) diff --git a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs index bdf29a86930..805a524030a 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs @@ -59,7 +59,7 @@ public void ResetRecords() /// public virtual void RenderAction( IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates ) { @@ -79,7 +79,7 @@ IAccount nextStates /// public virtual void RenderActionError( IValue action, - IActionContext context, + IActionRenderContext context, Exception exception ) => _records.Add( diff --git a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs index 4b7ba015ea0..56e702723a8 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs @@ -39,7 +39,7 @@ protected ActionBase( long index, string stackTrace, IValue action, - IActionContext context, + IActionRenderContext context, bool unrender = false ) : base(index, stackTrace) @@ -57,7 +57,7 @@ protected ActionBase( /// /// The action evaluation context. /// - public IActionContext Context { get; } + public IActionRenderContext Context { get; } /// /// Whether it is not an unrender event, but a render event. @@ -93,7 +93,7 @@ public ActionSuccess( long index, string stackTrace, IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates, bool unrender = false ) @@ -129,7 +129,7 @@ public ActionError( long index, string stackTrace, IValue action, - IActionContext context, + IActionRenderContext context, Exception exception, bool unrender = false ) diff --git a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs index 36f7f864319..81177bebf90 100644 --- a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs @@ -40,11 +40,10 @@ public override void RenderBlock(Block oldTip, Block newTip) Validate(); } - /// + /// public override void RenderAction( IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates ) { @@ -52,11 +51,10 @@ IAccount nextStates Validate(); } - /// + /// public override void RenderActionError( IValue action, - IActionContext context, + IActionRenderContext context, Exception exception ) { diff --git a/Libplanet/Blockchain/Renderers/IActionRenderer.cs b/Libplanet/Blockchain/Renderers/IActionRenderer.cs index 50d527865d8..08a3a71ea98 100644 --- a/Libplanet/Blockchain/Renderers/IActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/IActionRenderer.cs @@ -17,8 +17,8 @@ namespace Libplanet.Blockchain.Renderers /// /// (one time) /// - /// - /// & (zero or more + /// + /// & (zero or more /// times) /// /// (one time) @@ -56,18 +56,17 @@ public interface IActionRenderer : IRenderer /// It is guaranteed to be called only once for an , /// and only after applied to the blockchain, unless an exception is thrown during executing /// the (in that case is called instead) or + /// cref="RenderActionError"/> is called instead) or /// once the has been unrendered. /// Also note that this method is invoked after method is called /// (where its second parameter newTip contains a transaction the belongs to). /// - void RenderAction(IValue action, IActionContext context, IAccount nextStates); + void RenderAction(IValue action, IActionRenderContext context, IAccount nextStates); /// - /// Does the similar things to , except that this method + /// Does the similar things to , except that this method /// is invoked when has terminated with an exception. /// /// An action which threw an exception during execution. @@ -83,7 +82,7 @@ public interface IActionRenderer : IRenderer /// (where its second parameter newTip contains a transaction the belongs to). /// - void RenderActionError(IValue action, IActionContext context, Exception exception); + void RenderActionError(IValue action, IActionRenderContext context, Exception exception); /// /// Does things that should be done right all actions in a new are diff --git a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs index 45a0d776350..14097746632 100644 --- a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs @@ -67,11 +67,10 @@ Block newTip ActionRenderer.RenderBlockEnd ); - /// + /// public void RenderAction( IValue action, - IActionContext context, + IActionRenderContext context, IAccount nextStates ) => LogActionRendering( @@ -81,11 +80,10 @@ IAccount nextStates () => ActionRenderer.RenderAction(action, context, nextStates) ); - /// + /// public void RenderActionError( IValue action, - IActionContext context, + IActionRenderContext context, Exception exception ) => LogActionRendering( @@ -98,7 +96,7 @@ Exception exception private void LogActionRendering( string methodName, IValue action, - IActionContext context, + IActionRenderContext context, System.Action callback ) { From 725b16713a69fa7314c07512433182062637dd3a Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Mon, 18 Sep 2023 13:27:19 +0900 Subject: [PATCH 03/31] Render with StateRootHash --- .../Queries/StateQueryTest.cs | 2 +- .../Blockchain/BlockChainTest.Append.cs | 15 ++++++--- Libplanet/Blockchain/BlockChain.Swap.cs | 32 +++++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index 037f78fb1e0..e97f28ed96b 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -196,7 +196,7 @@ public ValidatorSet GetValidatorSet(BlockHash? offset) => public IAccountState GetAccountState(BlockHash? offset) => new MockAccount(offset); - public IAccountState GetAccountState(HashDigest? offset) => new MockAccount(null); + public IAccountState GetAccountState(HashDigest? hash) => new MockAccount(null); public ITrie GetTrie(BlockHash? offset) { diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 7bf98ff7fa8..57dabf652f1 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -80,7 +80,8 @@ Func getTxExecution Assert.Equal(2, renders[0].Context.BlockIndex); Assert.Equal( new IValue[] { null, null, null, null, (Integer)1 }, - addresses.Select(renders[0].Context.PreviousState.GetState) + addresses.Select( + _blockChain.GetAccountState(renders[0].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", null, null, null, (Integer)1 }, @@ -90,7 +91,8 @@ Func getTxExecution Assert.Equal(2, renders[1].Context.BlockIndex); Assert.Equal( addresses.Select(renders[0].NextStates.GetState), - addresses.Select(renders[1].Context.PreviousState.GetState) + addresses.Select( + _blockChain.GetAccountState(renders[1].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", null, null, (Integer)1 }, @@ -100,7 +102,8 @@ Func getTxExecution Assert.Equal(2, renders[2].Context.BlockIndex); Assert.Equal( addresses.Select(renders[1].NextStates.GetState), - addresses.Select(renders[2].Context.PreviousState.GetState) + addresses.Select( + _blockChain.GetAccountState(renders[2].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", null, (Integer)1 }, @@ -110,7 +113,8 @@ Func getTxExecution Assert.Equal(2, renders[3].Context.BlockIndex); Assert.Equal( addresses.Select(renders[2].NextStates.GetState), - addresses.Select(renders[3].Context.PreviousState.GetState) + addresses.Select( + _blockChain.GetAccountState(renders[3].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] @@ -137,7 +141,8 @@ Func getTxExecution ); Assert.Equal( (Integer)1, - (Integer)blockRenders[1].Context.PreviousState.GetState(minerAddress) + (Integer)_blockChain.GetAccountState(blockRenders[1].Context.PreviousState) + .GetState(minerAddress) ); Assert.Equal( (Integer)2, diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 7525fc1d81e..7ecc48dddb6 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -5,8 +5,10 @@ using System.Diagnostics; using System.Linq; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Blockchain.Renderers; using Libplanet.Store; +using Libplanet.Store.Trie; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -219,6 +221,7 @@ internal long RenderActions( } long count = 0; + ITrie trie = GetAccountState(block.PreviousHash).Trie; foreach (var evaluation in evaluations) { if (evaluation.InputContext.BlockAction && Policy.BlockAction is null) @@ -232,17 +235,42 @@ internal long RenderActions( { renderer.RenderAction( evaluation.Action, - new ActionRenderContext(evaluation.InputContext.GetUnconsumedContext()), + new ActionRenderContext( + signer: evaluation.InputContext.Signer, + txId: evaluation.InputContext.TxId, + miner: evaluation.InputContext.Miner, + blockIndex: evaluation.InputContext.BlockIndex, + blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, + rehearsal: evaluation.InputContext.Rehearsal, + previousState: trie.Hash, + random: evaluation.InputContext.GetUnconsumedContext().Random, + blockAction: evaluation.InputContext.BlockAction), evaluation.OutputState); } else { renderer.RenderActionError( evaluation.Action, - new ActionRenderContext(evaluation.InputContext.GetUnconsumedContext()), + new ActionRenderContext( + signer: evaluation.InputContext.Signer, + txId: evaluation.InputContext.TxId, + miner: evaluation.InputContext.Miner, + blockIndex: evaluation.InputContext.BlockIndex, + blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, + rehearsal: evaluation.InputContext.Rehearsal, + previousState: trie.Hash, + random: evaluation.InputContext.GetUnconsumedContext().Random, + blockAction: evaluation.InputContext.BlockAction), evaluation.Exception); } + foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) + { + trie = trie.Set(kv.Key, kv.Value); + } + + trie = StateStore.Commit(trie); + count++; } } From 22278b2f685b047b90c1d0e7d91c565e35236b59 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 19 Sep 2023 09:40:37 +0900 Subject: [PATCH 04/31] Use HashDigest instead of IAccount --- Libplanet.Action.Tests/Sys/InitializeTest.cs | 6 ++-- Libplanet.Tests/Action/ActionEvaluatorTest.cs | 4 +-- .../Blockchain/BlockChainTest.Append.cs | 20 ++++++----- Libplanet.Tests/Blockchain/BlockChainTest.cs | 4 +-- .../Renderers/AnonymousActionRendererTest.cs | 16 +++++---- .../Renderers/LoggedActionRendererTest.cs | 33 ++++++++++--------- Libplanet.Tests/Store/TrieStateStoreTest.cs | 4 +-- Libplanet/Blockchain/BlockChain.Swap.cs | 18 +++++----- .../Renderers/AnonymousActionRenderer.cs | 17 +++++----- .../Renderers/AtomicActionRenderer.cs | 15 +++++---- .../Debug/RecordingActionRenderer.cs | 7 ++-- .../Renderers/Debug/RenderRecord.cs | 11 ++++--- .../Debug/ValidatingActionRenderer.cs | 7 ++-- .../Blockchain/Renderers/IActionRenderer.cs | 12 ++++--- .../Renderers/LoggedActionRenderer.cs | 7 ++-- 15 files changed, 99 insertions(+), 82 deletions(-) diff --git a/Libplanet.Action.Tests/Sys/InitializeTest.cs b/Libplanet.Action.Tests/Sys/InitializeTest.cs index ff4243e9336..5c6bea6328c 100644 --- a/Libplanet.Action.Tests/Sys/InitializeTest.cs +++ b/Libplanet.Action.Tests/Sys/InitializeTest.cs @@ -59,10 +59,10 @@ public void Execute() validatorSet: _validatorSet ); - var nextStates = initialize.Execute(context); + var nextState = initialize.Execute(context); - Assert.Equal(_validatorSet, nextStates.GetValidatorSet()); - Assert.Equal(_states[default], nextStates.GetState(default)); + Assert.Equal(_validatorSet, nextState.GetValidatorSet()); + Assert.Equal(_states[default], nextState.GetState(default)); } [Fact] diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index 57432ae30ec..45eb15af07b 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -638,12 +638,12 @@ public void EvaluateTxResultThrowingException() lastCommit: CreateBlockCommit(hash, 122, 0)), transactions: txs).Propose(); IAccount previousState = actionEvaluator.PrepareInitialDelta(block); - var nextStates = actionEvaluator.EvaluateTx( + var nextState = actionEvaluator.EvaluateTx( blockHeader: block, tx: tx, previousState: previousState).Last().OutputState; - Assert.Empty(nextStates.GetUpdatedStates()); + Assert.Empty(nextState.GetUpdatedStates()); } [Fact] diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 57dabf652f1..30ffcf280df 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -85,34 +85,34 @@ Func getTxExecution ); Assert.Equal( new IValue[] { (Text)"foo", null, null, null, (Integer)1 }, - addresses.Select(renders[0].NextStates.GetState) + addresses.Select(_blockChain.GetAccountState(renders[0].NextState).GetState) ); Assert.Equal("bar", actions[1].Item); Assert.Equal(2, renders[1].Context.BlockIndex); Assert.Equal( - addresses.Select(renders[0].NextStates.GetState), + addresses.Select(_blockChain.GetAccountState(renders[0].NextState).GetState), addresses.Select( _blockChain.GetAccountState(renders[1].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", null, null, (Integer)1 }, - addresses.Select(renders[1].NextStates.GetState) + addresses.Select(_blockChain.GetAccountState(renders[1].NextState).GetState) ); Assert.Equal("baz", actions[2].Item); Assert.Equal(2, renders[2].Context.BlockIndex); Assert.Equal( - addresses.Select(renders[1].NextStates.GetState), + addresses.Select(_blockChain.GetAccountState(renders[1].NextState).GetState), addresses.Select( _blockChain.GetAccountState(renders[2].Context.PreviousState).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", null, (Integer)1 }, - addresses.Select(renders[2].NextStates.GetState) + addresses.Select(_blockChain.GetAccountState(renders[2].NextState).GetState) ); Assert.Equal("qux", actions[3].Item); Assert.Equal(2, renders[3].Context.BlockIndex); Assert.Equal( - addresses.Select(renders[2].NextStates.GetState), + addresses.Select(_blockChain.GetAccountState(renders[2].NextState).GetState), addresses.Select( _blockChain.GetAccountState(renders[3].Context.PreviousState).GetState) ); @@ -121,7 +121,7 @@ Func getTxExecution { (Text)"foo", (Text)"bar", (Text)"baz", (Text)"qux", (Integer)1, }, - renders[3].NextStates.GetStates(addresses) + _blockChain.GetAccountState(renders[3].NextState).GetStates(addresses) ); Address minerAddress = addresses[4]; @@ -137,7 +137,8 @@ Func getTxExecution Assert.Equal( (Integer)1, - (Integer)blockRenders[0].NextStates.GetState(minerAddress) + (Integer)_blockChain.GetAccountState( + blockRenders[0].NextState).GetState(minerAddress) ); Assert.Equal( (Integer)1, @@ -146,7 +147,8 @@ Func getTxExecution ); Assert.Equal( (Integer)2, - (Integer)blockRenders[1].NextStates.GetState(minerAddress) + (Integer)_blockChain.GetAccountState( + blockRenders[1].NextState).GetState(minerAddress) ); foreach (Transaction tx in txs) diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index e5b833820f8..b05da227ad1 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -311,7 +311,7 @@ public void ActionRenderersHaveDistinctContexts() new LoggedActionRenderer( new AnonymousActionRenderer { - ActionRenderer = (act, context, nextStates) => + ActionRenderer = (act, context, nextState) => // Consuming the random state through IRandom.Next() should not // affect contexts passed to other action renderers. generatedRandomValueLogs.Add(context.Random.Next()), @@ -382,7 +382,7 @@ public void RenderActionsAfterAppendComplete() IActionRenderer renderer = new AnonymousActionRenderer { - ActionRenderer = (a, __, nextStates) => + ActionRenderer = (a, __, nextState) => { if (!(a is Dictionary dictionary && dictionary.TryGetValue((Text)"type_id", out IValue typeId) && diff --git a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs index 22beb546cd9..a6b818fb7a0 100644 --- a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs @@ -1,10 +1,12 @@ using System; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Action.Tests.Mocks; using Libplanet.Blockchain.Renderers; +using Libplanet.Common; using Libplanet.Types.Blocks; using Xunit; @@ -41,11 +43,11 @@ public class AnonymousActionRendererTest [Fact] public void ActionRenderer() { - (IValue, IActionRenderContext, IAccount)? record = null; + (IValue, IActionRenderContext, HashDigest)? record = null; var renderer = new AnonymousActionRenderer { - ActionRenderer = (action, context, nextStates) => - record = (action, context, nextStates), + ActionRenderer = (action, context, nextState) => + record = (action, context, nextState), }; renderer.RenderActionError(_action, _actionContext, _exception); @@ -53,11 +55,11 @@ record = (action, context, nextStates), renderer.RenderBlock(_genesis, _blockA); Assert.Null(record); - renderer.RenderAction(_action, _actionContext, _account); + renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); Assert.NotNull(record); Assert.Same(_action, record?.Item1); Assert.Same(_actionContext, record?.Item2); - Assert.Same(_account, record?.Item3); + Assert.Equal(_account.Trie.Hash, record?.Item3); } [Fact] @@ -70,7 +72,7 @@ public void ActionErrorRenderer() record = (action, context, exception), }; - renderer.RenderAction(_action, _actionContext, _account); + renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); Assert.Null(record); renderer.RenderBlock(_genesis, _blockA); Assert.Null(record); @@ -91,7 +93,7 @@ public void BlockRenderer() BlockRenderer = (oldTip, newTip) => record = (oldTip, newTip), }; - renderer.RenderAction(_action, _actionContext, _account); + renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); Assert.Null(record); renderer.RenderActionError(_action, _actionContext, _exception); Assert.Null(record); diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index 1c9d4e66db2..593351f5006 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Action.Tests.Mocks; using Libplanet.Blockchain.Renderers; +using Libplanet.Common; using Libplanet.Types.Blocks; using Serilog; using Serilog.Events; @@ -23,8 +25,6 @@ public class LoggedActionRendererTest : IDisposable private static IAccount _account = new Account(MockAccountState.Empty); - private static Exception _exception = new Exception(); - private static Block _genesis = TestUtils.ProposeGenesisBlock(TestUtils.GenesisProposer); @@ -104,20 +104,21 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) } else { - Action render = (action, cxt, next) => - { - LogEvent[] logs = LogEvents.ToArray(); - Assert.Single(logs); - firstLog = logs[0]; - Assert.Same(_action, action); - Assert.Same(actionContext, cxt); - Assert.Same(_account, next); - called = true; - if (exception) + Action> render = + (action, cxt, next) => { - throw new ThrowException.SomeException(string.Empty); - } - }; + LogEvent[] logs = LogEvents.ToArray(); + Assert.Single(logs); + firstLog = logs[0]; + Assert.Same(_action, action); + Assert.Same(actionContext, cxt); + Assert.Equal(_account.Trie.Hash, next); + called = true; + if (exception) + { + throw new ThrowException.SomeException(string.Empty); + } + }; actionRenderer = new AnonymousActionRenderer { ActionRenderer = render, @@ -146,7 +147,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) } else { - actionRenderer.RenderAction(_action, actionContext, _account); + actionRenderer.RenderAction(_action, actionContext, _account.Trie.Hash); } } catch (ThrowException.SomeException e) diff --git a/Libplanet.Tests/Store/TrieStateStoreTest.cs b/Libplanet.Tests/Store/TrieStateStoreTest.cs index 5bfd9b65ae6..b99ee445ffc 100644 --- a/Libplanet.Tests/Store/TrieStateStoreTest.cs +++ b/Libplanet.Tests/Store/TrieStateStoreTest.cs @@ -80,9 +80,9 @@ public void PruneStates() ITrie first = stateStore.Commit(null, values); int prevStatesCount = _stateKeyValueStore.ListKeys().Count(); - ImmutableDictionary nextStates = + ImmutableDictionary nextState = values.SetItem(new KeyBytes("foo"), (Binary)GetRandomBytes(4096)); - ITrie second = stateStore.Commit(first.Hash, nextStates); + ITrie second = stateStore.Commit(first.Hash, nextState); // foo = 0x666f6f // updated branch node (0x6, aka root) + updated branch node (0x66) + diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 7ecc48dddb6..33f87ede385 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -229,6 +229,14 @@ internal long RenderActions( continue; } + ITrie nextTrie = trie; + foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) + { + nextTrie = nextTrie.Set(kv.Key, kv.Value); + } + + nextTrie = StateStore.Commit(nextTrie); + foreach (IActionRenderer renderer in ActionRenderers) { if (evaluation.Exception is null) @@ -245,7 +253,7 @@ internal long RenderActions( previousState: trie.Hash, random: evaluation.InputContext.GetUnconsumedContext().Random, blockAction: evaluation.InputContext.BlockAction), - evaluation.OutputState); + nextTrie.Hash); } else { @@ -264,13 +272,7 @@ internal long RenderActions( evaluation.Exception); } - foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) - { - trie = trie.Set(kv.Key, kv.Value); - } - - trie = StateStore.Commit(trie); - + trie = nextTrie; count++; } } diff --git a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs index 366b26fc35d..9b95cac7fa4 100644 --- a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs @@ -1,7 +1,8 @@ using System; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; namespace Libplanet.Blockchain.Renderers @@ -18,7 +19,7 @@ namespace Libplanet.Blockchain.Renderers /// + /// ActionRenderer = (action, context, nextState) => /// { /// // Implement RenderAction() here. /// }; @@ -29,9 +30,10 @@ public sealed class AnonymousActionRenderer : AnonymousRenderer, IActionRenderer { /// /// A callback function to be invoked together with - /// . + /// . /// - public Action? ActionRenderer { get; set; } + public Action>? ActionRenderer + { get; set; } /// /// A callback function to be invoked together with @@ -45,14 +47,13 @@ public sealed class AnonymousActionRenderer : AnonymousRenderer, IActionRenderer /// public Action? BlockEndRenderer { get; set; } - /// + /// public void RenderAction( IValue action, IActionRenderContext context, - IAccount nextStates + HashDigest nextState ) => - ActionRenderer?.Invoke(action, context, nextStates); + ActionRenderer?.Invoke(action, context, nextState); /// diff --git a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs index 0532abb0b5d..646bf8f001f 100644 --- a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -20,7 +21,7 @@ namespace Libplanet.Blockchain.Renderers /// public sealed class AtomicActionRenderer : IActionRenderer { - private readonly List<(IValue, IActionRenderContext, IAccount)> _eventBuffer; + private readonly List<(IValue, IActionRenderContext, HashDigest)> _eventBuffer; private TxId? _lastTxId; private bool _errored; @@ -35,7 +36,7 @@ public AtomicActionRenderer(IActionRenderer actionRenderer) { ActionRenderer = actionRenderer; _lastTxId = null; - _eventBuffer = new List<(IValue, IActionRenderContext, IAccount)>(); + _eventBuffer = new List<(IValue, IActionRenderContext, HashDigest)>(); _errored = false; } @@ -62,7 +63,7 @@ public void RenderBlockEnd(Block oldTip, Block newTip) public void RenderAction( IValue action, IActionRenderContext context, - IAccount nextStates + HashDigest nextState ) { if (!context.TxId.Equals(_lastTxId)) @@ -72,11 +73,11 @@ IAccount nextStates if (context.TxId is null) { - ActionRenderer.RenderAction(action, context, nextStates); + ActionRenderer.RenderAction(action, context, nextState); } else if (!_errored) { - _eventBuffer.Add((action, context, nextStates)); + _eventBuffer.Add((action, context, nextState)); } } @@ -103,7 +104,7 @@ public void RenderActionError( private void FlushBuffer( TxId? newTxId, - Action render + Action> render ) { if (!_errored) diff --git a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs index 805a524030a..e04ace87f64 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; using Serilog; @@ -60,7 +61,7 @@ public void ResetRecords() public virtual void RenderAction( IValue action, IActionRenderContext context, - IAccount nextStates + HashDigest nextState ) { _records.Add( @@ -69,7 +70,7 @@ IAccount nextStates stackTrace: RemoveFirstLine(Environment.StackTrace).TrimEnd(), action: action, context: context, - nextStates: nextStates + nextState: nextState ) ); diff --git a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs index 56e702723a8..78d12a04e39 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs @@ -1,7 +1,8 @@ using System; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; namespace Libplanet.Blockchain.Renderers.Debug @@ -87,25 +88,25 @@ public class ActionSuccess : ActionBase /// The stack trace of the render event. /// The rendered action. /// The action evaluation context. - /// The resulting states after the action is evaluated. + /// The resulting state after the action is evaluated. /// Whether it is an unrender event. public ActionSuccess( long index, string stackTrace, IValue action, IActionRenderContext context, - IAccount nextStates, + HashDigest nextState, bool unrender = false ) : base(index, stackTrace, action, context, unrender: unrender) { - NextStates = nextStates; + NextState = nextState; } /// /// The resulting states after the action is evaluated. /// - public IAccount NextStates { get; } + public HashDigest NextState { get; } /// public override string ToString() => $"{base.ToString()} [success]"; diff --git a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs index 81177bebf90..f8be6ab7f24 100644 --- a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; namespace Libplanet.Blockchain.Renderers.Debug @@ -44,10 +45,10 @@ public override void RenderBlock(Block oldTip, Block newTip) public override void RenderAction( IValue action, IActionRenderContext context, - IAccount nextStates + HashDigest nextState ) { - base.RenderAction(action, context, nextStates); + base.RenderAction(action, context, nextState); Validate(); } diff --git a/Libplanet/Blockchain/Renderers/IActionRenderer.cs b/Libplanet/Blockchain/Renderers/IActionRenderer.cs index 08a3a71ea98..dce1073cabe 100644 --- a/Libplanet/Blockchain/Renderers/IActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/IActionRenderer.cs @@ -1,7 +1,8 @@ using System; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Store; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -48,8 +49,8 @@ public interface IActionRenderer : IRenderer /// the 's method. /// That means are the states right /// before this action executed. For the states after this action executed, - /// use the argument instead. - /// The states right after this action executed, + /// use the argument instead. + /// The state root hash right after this action executed, /// which means it is equivalent to the states 's /// method returned. /// @@ -63,7 +64,10 @@ public interface IActionRenderer : IRenderer /// (where its second parameter newTip contains a transaction the belongs to). /// - void RenderAction(IValue action, IActionRenderContext context, IAccount nextStates); + void RenderAction( + IValue action, + IActionRenderContext context, + HashDigest nextState); /// /// Does the similar things to , except that this method diff --git a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs index 14097746632..b0c715bbe43 100644 --- a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs @@ -1,7 +1,8 @@ using System; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Types.Blocks; using Serilog; using Serilog.Events; @@ -71,13 +72,13 @@ Block newTip public void RenderAction( IValue action, IActionRenderContext context, - IAccount nextStates + HashDigest nextState ) => LogActionRendering( nameof(RenderAction), action, context, - () => ActionRenderer.RenderAction(action, context, nextStates) + () => ActionRenderer.RenderAction(action, context, nextState) ); /// From 0fd63b707195acad0f115782cea32403441527ef Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 19 Sep 2023 10:36:30 +0900 Subject: [PATCH 05/31] Changelog --- CHANGES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d9784b55f4d..8b9e5a89637 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,12 @@ To be released. - Added `IBlockChainStates.GetAccountState(HashDigest?)` interface method. [[#3425]] + - Changed `IActionRenderer.RenderAction(IValue, IActionContext, IAccount)` + to `IActionRenderer.RenderAction(IValue, IActionRenderContext, + HashDigest)`. [[#3427]] + - Changed `IActionRenderer.RenderActionError(IValue, IActionContext, + Exception)` to `IActionRenderer.RenderActionError(IValue, + IActionRenderContext, Exception)`. [[#3427]] ### Backward-incompatible network protocol changes @@ -19,6 +25,8 @@ To be released. ### Added APIs + - Added `IActionRenderContext` interface. [[#3427]] + ### Behavioral changes ### Bug fixes @@ -28,6 +36,7 @@ To be released. ### CLI tools [#3425]: https://github.com/planetarium/libplanet/pull/3425 +[#3427]: https://github.com/planetarium/libplanet/pull/3427 Version 3.3.1 From c9dae8452e7996b024d29a26340e33514c72a569 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 14 Sep 2023 10:36:30 +0900 Subject: [PATCH 06/31] Initial implementation of AccountDiff --- Libplanet.Action/State/AccountDiff.cs | 303 ++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 Libplanet.Action/State/AccountDiff.cs diff --git a/Libplanet.Action/State/AccountDiff.cs b/Libplanet.Action/State/AccountDiff.cs new file mode 100644 index 00000000000..66fa45822d2 --- /dev/null +++ b/Libplanet.Action/State/AccountDiff.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; + +namespace Libplanet.Action.State +{ + public class AccountDiff + { + private static readonly int _addressKeyLength = Address.Size * 2; + + private static readonly int _currencyKeyLength = HashDigest.Size * 2; + + private static readonly int _stateKeyLength = _addressKeyLength; + + private static readonly int _fungibleAssetKeyLength = + _addressKeyLength + _currencyKeyLength + 2; + + private static readonly int _totalSupplyKeyLength = _currencyKeyLength + 2; + + private static readonly int _validatorSetKeyLength = 3; + + private static readonly ImmutableDictionary _reverseConversionTable = + new Dictionary() + { + [48] = 0, // '0' + [49] = 1, // '1' + [50] = 2, // '2' + [51] = 3, // '3' + [52] = 4, // '4' + [53] = 5, // '5' + [54] = 6, // '6' + [55] = 7, // '7' + [56] = 8, // '8' + [57] = 9, // '9' + [97] = 10, // 'a' + [98] = 11, // 'b' + [99] = 12, // 'c' + [100] = 13, // 'd' + [101] = 14, // 'e' + [102] = 15, // 'f' + }.ToImmutableDictionary(); + + private AccountDiff( + ImmutableDictionary stateDiff, + ImmutableDictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> + fungibleAssetValueDiff, + ImmutableDictionary + totalSupplyDiff, + (ValidatorSet, ValidatorSet)? validatorSetDiff) + { + StateDiffs = stateDiff; + FungibleAssetValueDiffs = fungibleAssetValueDiff; + TotalSupplyDiffs = totalSupplyDiff; + ValidatorSetDiff = validatorSetDiff; + } + + public ImmutableDictionary StateDiffs { get; } + + public ImmutableDictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> + FungibleAssetValueDiffs { get; } + + public ImmutableDictionary + TotalSupplyDiffs { get; } + + public (ValidatorSet, ValidatorSet)? ValidatorSetDiff { get; } + + public static AccountDiff Create(IAccountState source, IAccountState target) + => Create(source.Trie, target.Trie); + + // NOTE: interpret not actual. + public static AccountDiff Create(ITrie source, ITrie target) + { + var rawDiffs = source.Diff(target).ToList(); + + Dictionary stateDiffs = + new Dictionary(); + Dictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> favDiffs = + new Dictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)>(); + Dictionary totalSupplyDiffs = + new Dictionary(); + (ValidatorSet, ValidatorSet)? validatorSetDiff = null; + + foreach (var diff in rawDiffs) + { + // NOTE: Cannot use switch as some lengths cannot be derived as const. + if (diff.Path.Length == _stateKeyLength) + { + var sd = ToStateDiff(diff); + stateDiffs[sd.Address] = (sd.TargetValue, sd.SourceValue); + } + else if (diff.Path.Length == _fungibleAssetKeyLength) + { + var favd = ToFAVDiff(diff); + + // NOTE: Only add when different. Actual stored data may be different + // as 0 value can also be represented as null. + if (!favd.SourceValue.Equals(favd.TargetValue)) + { + favDiffs[(favd.Address, favd.Currency)] = + (favd.TargetValue, favd.SourceValue); + } + } + else if (diff.Path.Length == _totalSupplyKeyLength) + { + var tsd = ToTotalSupplyDiff(diff); + + // NOTE: Only add when different. Actual stored data may be different + // as 0 value can also be represented as null. + if (!tsd.SourceValue.Equals(tsd.TargetValue)) + { + totalSupplyDiffs[tsd.Currency] = (tsd.TargetValue, tsd.SourceValue); + } + } + else if (diff.Path.Length == _validatorSetKeyLength) + { + var vsd = ToValidatorSetDiff(diff); + + // NOTE: Only set when different. Actual stored data may be different + // as empty validator set can also be represented as null. + if (!vsd.SourceValue.Equals(vsd.TargetValue)) + { + validatorSetDiff = (vsd.TargetValue, vsd.SourceValue); + } + } + else + { + throw new ArgumentException( + $"Encountered different values at an invalid location: {diff.Path}"); + } + } + + return new AccountDiff( + stateDiffs.ToImmutableDictionary(), + favDiffs.ToImmutableDictionary(), + totalSupplyDiffs.ToImmutableDictionary(), + validatorSetDiff); + } + + internal static (Address Address, IValue? TargetValue, IValue SourceValue) + ToStateDiff((KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) + { + return ( + ToAddress(encoded.Path.ToByteArray()), + encoded.TargetValue, + encoded.SourceValue); + } + + internal static ( + Address Address, + Currency Currency, + FungibleAssetValue TargetValue, + FungibleAssetValue SourceValue) ToFAVDiff( + (KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) + { + Address address = + ToAddress( + encoded.Path.ByteArray.Skip(1).Take(_addressKeyLength).ToArray()); + HashDigest currencyHash = + ToCurrencyHash( + encoded.Path.ByteArray + .Skip(_addressKeyLength + 2) + .Take(_currencyKeyLength) + .ToArray()); + FungibleAssetValue sourceFAV = new FungibleAssetValue(encoded.SourceValue); + Currency currency = sourceFAV.Currency; + FungibleAssetValue targetFAV = encoded.TargetValue is { } value + ? new FungibleAssetValue(value) + : FungibleAssetValue.FromRawValue(sourceFAV.Currency, 0); + + if (!currency.Hash.Equals(currencyHash)) + { + throw new ArgumentException( + $"The internal trie path {currencyHash} for a stored FAV does not match " + + $"the hash {currency.Hash} of the FAV {currency.Ticker}"); + } + else if (!currency.Equals(targetFAV.Currency)) + { + throw new ArgumentException( + $"The currency of the FAV stored in target {targetFAV.Currency} " + + $"does match the currency of the FAV stored in source {sourceFAV.Currency}"); + } + + return (address, currency, targetFAV, sourceFAV); + } + + internal static ( + Currency Currency, + FungibleAssetValue TargetValue, + FungibleAssetValue SourceValue) ToTotalSupplyDiff( + (KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) + { + HashDigest currencyHash = + ToCurrencyHash( + encoded.Path.ByteArray + .Skip(_addressKeyLength + 2) + .Take(_currencyKeyLength) + .ToArray()); + FungibleAssetValue sourceFAV = new FungibleAssetValue(encoded.SourceValue); + Currency currency = sourceFAV.Currency; + FungibleAssetValue targetFAV = encoded.TargetValue is { } value + ? new FungibleAssetValue(value) + : FungibleAssetValue.FromRawValue(sourceFAV.Currency, 0); + + if (!currency.Hash.Equals(currencyHash)) + { + throw new ArgumentException( + $"The internal trie path {currencyHash} for a stored FAV does not match " + + $"the hash {currency.Hash} of the FAV {currency.Ticker}"); + } + else if (!currency.Equals(targetFAV.Currency)) + { + throw new ArgumentException( + $"The currency of the FAV stored in target {targetFAV.Currency} " + + $"does match the currency of the FAV stored in source {sourceFAV.Currency}"); + } + + return (currency, targetFAV, sourceFAV); + } + + internal static (ValidatorSet TargetValue, ValidatorSet SourceValue) + ToValidatorSetDiff( + (KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) + { + if (encoded.Path.Equals(KeyConverters.ValidatorSetKey)) + { + ValidatorSet sourceVS = new ValidatorSet(encoded.SourceValue); + ValidatorSet targetVS = encoded.TargetValue is { } value + ? new ValidatorSet(value) + : new ValidatorSet(); + return (targetVS, sourceVS); + } + else + { + throw new ArgumentException( + $"Encountered different values at an invalid location: {encoded.Path}"); + } + } + + internal static Address FromStateKey(KeyBytes key) + { + if (key.Length != _stateKeyLength) + { + throw new ArgumentException( + $"Given {nameof(key)} must be of length {_stateKeyLength}: {key.Length}"); + } + + byte[] buffer = new byte[Address.Size]; + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = Pack(key.ByteArray[i * 2], key.ByteArray[i * 2 + 1]); + } + + return new Address(buffer); + } + + internal static Address ToAddress(byte[] bytes) + { + if (bytes.Length != _stateKeyLength) + { + throw new ArgumentException( + $"Given {nameof(bytes)} must be of length {_stateKeyLength}: {bytes.Length}"); + } + + byte[] buffer = new byte[Address.Size]; + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = Pack(bytes[i * 2], bytes[i * 2 + 1]); + } + + return new Address(buffer); + } + + internal static HashDigest ToCurrencyHash(byte[] bytes) + { + var expectedLength = HashDigest.Size * 2; + if (bytes.Length != expectedLength) + { + throw new ArgumentException( + $"Given {nameof(bytes)} must be of length {_stateKeyLength}: {bytes.Length}"); + } + + byte[] buffer = new byte[HashDigest.Size]; + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = Pack(bytes[i * 2], bytes[i * 2 + 1]); + } + + return new HashDigest(buffer); + } + + // FIXME: Assumes both x and y are less than 16. + private static byte Pack(byte x, byte y) => + (byte)((_reverseConversionTable[x] << 4) + _reverseConversionTable[y]); + } +} From 85e54d2e434ec43cbbc94c7abe3ba8834e8c04be Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 14 Sep 2023 14:01:24 +0900 Subject: [PATCH 07/31] Docs --- Libplanet.Action/State/AccountDiff.cs | 57 +++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/Libplanet.Action/State/AccountDiff.cs b/Libplanet.Action/State/AccountDiff.cs index 66fa45822d2..1d48f61ee12 100644 --- a/Libplanet.Action/State/AccountDiff.cs +++ b/Libplanet.Action/State/AccountDiff.cs @@ -12,6 +12,36 @@ namespace Libplanet.Action.State { + /// + /// Represents a difference between two s. + /// This is an interpretation of a raw difference obtained by + /// from 's perspective. Keep in mind of the following properties: + /// + /// + /// Any value, which is equivalent to non-existant value in + /// the underlying storage, in the source is ignored. That is, even if + /// the value in the target and the value in the source are different while + /// the value in the source is , this will not be + /// part of the resulting . + /// + /// + /// Any value, again, which is equivalent to non-existant value + /// in the underlying storage, in the target for + /// and is interpreted accordingly. That is, + /// 0 amount of and empty + /// are used. This is in accordance with how + /// and would behave. + /// + /// + /// Due to the reason mentioned directly above, the size of + /// derived from may not be the same. Moreover, + /// an being empty does not guarantee + /// that the data are the same as is not capable of + /// distinguishing between and 0 + /// and so on and so forth. + /// + /// + /// public class AccountDiff { private static readonly int _addressKeyLength = Address.Size * 2; @@ -72,11 +102,32 @@ private AccountDiff( public (ValidatorSet, ValidatorSet)? ValidatorSetDiff { get; } - public static AccountDiff Create(IAccountState source, IAccountState target) + /// + /// Creates an instance from given parameters. + /// + /// The to use as the target. + /// The to use as the source. + /// An created from given parameters. + /// Note that the ordering of the parameters are flipped compared to + /// for syntactical reasons. + /// + /// + public static AccountDiff Create(IAccountState target, IAccountState source) => Create(source.Trie, target.Trie); - // NOTE: interpret not actual. - public static AccountDiff Create(ITrie source, ITrie target) + /// + /// Creates an instance from given parameters. + /// + /// The to use as the target. + /// The to use as the source. + /// An created from given parameters. + /// Thrown when the diff internally obtained from + /// cannot be properly interpreted. + /// Note that the ordering of the parameters are flipped compared to + /// for syntactical reasons. + /// + /// + public static AccountDiff Create(ITrie target, ITrie source) { var rawDiffs = source.Diff(target).ToList(); From 09dad05f94fc1bd476947b8f869c1a521ee15e71 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 15 Sep 2023 17:38:32 +0900 Subject: [PATCH 08/31] Change FAVs to HashDigest and Integer --- Libplanet.Action/State/AccountDiff.cs | 115 +++++++++++++------------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/Libplanet.Action/State/AccountDiff.cs b/Libplanet.Action/State/AccountDiff.cs index 1d48f61ee12..00a228ce5e7 100644 --- a/Libplanet.Action/State/AccountDiff.cs +++ b/Libplanet.Action/State/AccountDiff.cs @@ -14,7 +14,7 @@ namespace Libplanet.Action.State { /// /// Represents a difference between two s. - /// This is an interpretation of a raw difference obtained by + /// This is a partial interpretation of a raw difference obtained by /// from 's perspective. Keep in mind of the following properties: /// /// @@ -40,6 +40,13 @@ namespace Libplanet.Action.State /// distinguishing between and 0 /// and so on and so forth. /// + /// + /// As information is in the domain of an application using + /// this library, only the hash of a is directly stored in + /// the underlying storage. As such, each and + /// are handled as raw values, that is, as hash and + /// , for an . + /// /// /// public class AccountDiff @@ -80,9 +87,9 @@ public class AccountDiff private AccountDiff( ImmutableDictionary stateDiff, - ImmutableDictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> + ImmutableDictionary<(Address, HashDigest), (Integer, Integer)> fungibleAssetValueDiff, - ImmutableDictionary + ImmutableDictionary, (Integer, Integer)> totalSupplyDiff, (ValidatorSet, ValidatorSet)? validatorSetDiff) { @@ -94,10 +101,10 @@ private AccountDiff( public ImmutableDictionary StateDiffs { get; } - public ImmutableDictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> + public ImmutableDictionary<(Address, HashDigest), (Integer, Integer)> FungibleAssetValueDiffs { get; } - public ImmutableDictionary + public ImmutableDictionary, (Integer, Integer)> TotalSupplyDiffs { get; } public (ValidatorSet, ValidatorSet)? ValidatorSetDiff { get; } @@ -113,7 +120,7 @@ private AccountDiff( /// /// public static AccountDiff Create(IAccountState target, IAccountState source) - => Create(source.Trie, target.Trie); + => Create(target.Trie, source.Trie); /// /// Creates an instance from given parameters. @@ -133,10 +140,10 @@ public static AccountDiff Create(ITrie target, ITrie source) Dictionary stateDiffs = new Dictionary(); - Dictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)> favDiffs = - new Dictionary<(Address, Currency), (FungibleAssetValue, FungibleAssetValue)>(); - Dictionary totalSupplyDiffs = - new Dictionary(); + Dictionary<(Address, HashDigest), (Integer, Integer)> favDiffs = + new Dictionary<(Address, HashDigest), (Integer, Integer)>(); + Dictionary, (Integer, Integer)> totalSupplyDiffs = + new Dictionary, (Integer, Integer)>(); (ValidatorSet, ValidatorSet)? validatorSetDiff = null; foreach (var diff in rawDiffs) @@ -206,9 +213,9 @@ internal static (Address Address, IValue? TargetValue, IValue SourceValue) internal static ( Address Address, - Currency Currency, - FungibleAssetValue TargetValue, - FungibleAssetValue SourceValue) ToFAVDiff( + HashDigest Currency, + Integer TargetValue, + Integer SourceValue) ToFAVDiff( (KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) { Address address = @@ -220,60 +227,48 @@ internal static ( .Skip(_addressKeyLength + 2) .Take(_currencyKeyLength) .ToArray()); - FungibleAssetValue sourceFAV = new FungibleAssetValue(encoded.SourceValue); - Currency currency = sourceFAV.Currency; - FungibleAssetValue targetFAV = encoded.TargetValue is { } value - ? new FungibleAssetValue(value) - : FungibleAssetValue.FromRawValue(sourceFAV.Currency, 0); - - if (!currency.Hash.Equals(currencyHash)) - { - throw new ArgumentException( - $"The internal trie path {currencyHash} for a stored FAV does not match " + - $"the hash {currency.Hash} of the FAV {currency.Ticker}"); - } - else if (!currency.Equals(targetFAV.Currency)) - { - throw new ArgumentException( - $"The currency of the FAV stored in target {targetFAV.Currency} " + - $"does match the currency of the FAV stored in source {sourceFAV.Currency}"); - } - - return (address, currency, targetFAV, sourceFAV); + Integer sourceValue = encoded.SourceValue is Integer sourceInteger + ? sourceInteger + : throw new ArgumentException( + $"Expected an {nameof(Integer)} but encountered an invalid value " + + $"{encoded.SourceValue} at {encoded.Path}"); + Integer targetValue = encoded.TargetValue is { } value + ? value is Integer targetInteger + ? targetInteger + : throw new ArgumentException( + $"Expected an {nameof(Integer)} but encountered " + + $"an invalid value {encoded.TargetValue} at {encoded.Path}") + : new Integer(0); + + return (address, currencyHash, targetValue, sourceValue); } internal static ( - Currency Currency, - FungibleAssetValue TargetValue, - FungibleAssetValue SourceValue) ToTotalSupplyDiff( + HashDigest Currency, + Integer TargetValue, + Integer SourceValue) ToTotalSupplyDiff( (KeyBytes Path, IValue? TargetValue, IValue SourceValue) encoded) { HashDigest currencyHash = ToCurrencyHash( encoded.Path.ByteArray - .Skip(_addressKeyLength + 2) + .Skip(2) .Take(_currencyKeyLength) .ToArray()); - FungibleAssetValue sourceFAV = new FungibleAssetValue(encoded.SourceValue); - Currency currency = sourceFAV.Currency; - FungibleAssetValue targetFAV = encoded.TargetValue is { } value - ? new FungibleAssetValue(value) - : FungibleAssetValue.FromRawValue(sourceFAV.Currency, 0); - - if (!currency.Hash.Equals(currencyHash)) - { - throw new ArgumentException( - $"The internal trie path {currencyHash} for a stored FAV does not match " + - $"the hash {currency.Hash} of the FAV {currency.Ticker}"); - } - else if (!currency.Equals(targetFAV.Currency)) - { - throw new ArgumentException( - $"The currency of the FAV stored in target {targetFAV.Currency} " + - $"does match the currency of the FAV stored in source {sourceFAV.Currency}"); - } - - return (currency, targetFAV, sourceFAV); + Integer sourceValue = encoded.SourceValue is Integer sourceInteger + ? sourceInteger + : throw new ArgumentException( + $"Expected an {nameof(Integer)} but encountered an invalid value " + + $"{encoded.SourceValue} at {encoded.Path}"); + Integer targetValue = encoded.TargetValue is { } value + ? value is Integer targetInteger + ? targetInteger + : throw new ArgumentException( + $"Expected an {nameof(Integer)} but encountered " + + $"an invalid value {encoded.TargetValue} at {encoded.Path}") + : new Integer(0); + + return (currencyHash, targetValue, sourceValue); } internal static (ValidatorSet TargetValue, ValidatorSet SourceValue) @@ -329,13 +324,15 @@ internal static Address ToAddress(byte[] bytes) return new Address(buffer); } + // FIXME: This assumes to know that hash algorithm used by Currency is SHA1. internal static HashDigest ToCurrencyHash(byte[] bytes) { - var expectedLength = HashDigest.Size * 2; + var expectedLength = _currencyKeyLength; if (bytes.Length != expectedLength) { throw new ArgumentException( - $"Given {nameof(bytes)} must be of length {_stateKeyLength}: {bytes.Length}"); + $"Given {nameof(bytes)} must be of length {_currencyKeyLength}: " + + $"{bytes.Length}"); } byte[] buffer = new byte[HashDigest.Size]; From 71f5a6b340c67476d95edb5e831f950eeb68721a Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 15 Sep 2023 17:43:48 +0900 Subject: [PATCH 09/31] Added test --- Libplanet.Tests/Action/AccountDiffTest.cs | 172 ++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 Libplanet.Tests/Action/AccountDiffTest.cs diff --git a/Libplanet.Tests/Action/AccountDiffTest.cs b/Libplanet.Tests/Action/AccountDiffTest.cs new file mode 100644 index 00000000000..16c902ad210 --- /dev/null +++ b/Libplanet.Tests/Action/AccountDiffTest.cs @@ -0,0 +1,172 @@ +using System.Collections.Generic; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Xunit; + +namespace Libplanet.Tests.Action +{ + public class AccountDiffTest + { + public AccountDiffTest() + { + USD = Currency.Uncapped("USD", 2, null); + KRW = Currency.Uncapped("KRW", 0, null); + JPY = Currency.Uncapped("JPY", 0, null); + } + + public Currency USD { get; } + + public Currency KRW { get; } + + public Currency JPY { get; } + + [Fact] + public void EmptyAccountStateSource() + { + IStateStore stateStore = new TrieStateStore(new MemoryKeyValueStore()); + ITrie targetTrie = stateStore.GetStateRoot(null); + ITrie sourceTrie = stateStore.GetStateRoot(null); + + AccountDiff diff = AccountDiff.Create(targetTrie, sourceTrie); + Assert.Empty(diff.StateDiffs); + Assert.Empty(diff.FungibleAssetValueDiffs); + Assert.Empty(diff.TotalSupplyDiffs); + Assert.Null(diff.ValidatorSetDiff); + + IAccount targetAccount = new Account(new AccountState(targetTrie)); + PrivateKey signer = new PrivateKey(); + IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); + targetAccount = targetAccount.MintAsset( + context, signer.ToAddress(), new FungibleAssetValue(USD, 123, 45)); + targetAccount = targetAccount.SetState(signer.ToAddress(), new Text("Foo")); + + targetTrie = Commit(stateStore, targetTrie, targetAccount.Delta); + + diff = AccountDiff.Create(targetTrie, sourceTrie); + Assert.Empty(diff.StateDiffs); + Assert.Empty(diff.FungibleAssetValueDiffs); + Assert.Empty(diff.TotalSupplyDiffs); + Assert.Null(diff.ValidatorSetDiff); + } + + [Fact] + public void Diff() + { + IStateStore stateStore = new TrieStateStore(new MemoryKeyValueStore()); + ITrie targetTrie = stateStore.GetStateRoot(null); + ITrie sourceTrie = stateStore.GetStateRoot(null); + + Address addr1 = new Address(TestUtils.GetRandomBytes(Address.Size)); + Address addr2 = new Address(TestUtils.GetRandomBytes(Address.Size)); + Address addr3 = new Address(TestUtils.GetRandomBytes(Address.Size)); + + AccountDiff diff = AccountDiff.Create(targetTrie, sourceTrie); + Assert.Empty(diff.StateDiffs); + Assert.Empty(diff.FungibleAssetValueDiffs); + Assert.Empty(diff.TotalSupplyDiffs); + Assert.Null(diff.ValidatorSetDiff); + + IAccount targetAccount = new Account(new AccountState(targetTrie)); + PrivateKey signer = new PrivateKey(); + IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); + targetAccount = targetAccount.SetState(addr1, new Text("One")); + targetAccount = targetAccount.SetState(addr2, new Text("Two")); + targetAccount = targetAccount.MintAsset( + context, signer.ToAddress(), new FungibleAssetValue(USD, 123, 45)); + + targetTrie = Commit(stateStore, targetTrie, targetAccount.Delta); + sourceTrie = targetTrie; + + IAccount sourceAccount = new Account(new AccountState(sourceTrie)); + sourceAccount = sourceAccount.SetState(addr2, new Text("Two_")); + sourceAccount = sourceAccount.SetState(addr3, new Text("Three")); + sourceAccount = sourceAccount.MintAsset( + context, signer.ToAddress(), new FungibleAssetValue(USD, 456, 78)); + sourceAccount = sourceAccount.MintAsset( + context, signer.ToAddress(), new FungibleAssetValue(KRW, 10, 0)); + sourceAccount = sourceAccount.BurnAsset( + context, signer.ToAddress(), new FungibleAssetValue(KRW, 10, 0)); + sourceAccount = sourceAccount.MintAsset( + context, signer.ToAddress(), new FungibleAssetValue(JPY, 321, 0)); + sourceAccount = sourceAccount.SetValidator(new Validator(signer.PublicKey, 1)); + + sourceTrie = Commit(stateStore, sourceTrie, sourceAccount.Delta); + + diff = AccountDiff.Create(targetTrie, sourceTrie); + Assert.Equal(2, diff.StateDiffs.Count); + Assert.Equal((new Text("Two"), new Text("Two_")), diff.StateDiffs[addr2]); + Assert.Equal((null, new Text("Three")), diff.StateDiffs[addr3]); + + Assert.Equal(2, diff.FungibleAssetValueDiffs.Count); // KRW is treated as unchanged + Assert.Equal( + (new Integer(12345), new Integer(12345 + 45678)), + diff.FungibleAssetValueDiffs[(signer.ToAddress(), USD.Hash)]); + Assert.Equal( + (new Integer(0), new Integer(321)), + diff.FungibleAssetValueDiffs[(signer.ToAddress(), JPY.Hash)]); + + Assert.Equal(2, diff.TotalSupplyDiffs.Count); // KRW is treated as unchanged + Assert.Equal( + (new Integer(12345), new Integer(12345 + 45678)), + diff.TotalSupplyDiffs[USD.Hash]); + Assert.Equal( + (new Integer(0), new Integer(321)), + diff.TotalSupplyDiffs[JPY.Hash]); + + Assert.Equal( + ( + new ValidatorSet(), + new ValidatorSet(new List() { new Validator(signer.PublicKey, 1) }) + ), + diff.ValidatorSetDiff); + + diff = AccountDiff.Create(sourceTrie, targetTrie); + Assert.Single(diff.StateDiffs); // Note addr3 is not tracked + Assert.Equal((new Text("Two_"), new Text("Two")), diff.StateDiffs[addr2]); + Assert.Single(diff.FungibleAssetValueDiffs); // Only USD is tracked + Assert.Equal( + (new Integer(12345 + 45678), new Integer(12345)), + diff.FungibleAssetValueDiffs[(signer.ToAddress(), USD.Hash)]); + Assert.Single(diff.TotalSupplyDiffs); // Only USD is tracked + Assert.Equal( + (new Integer(12345 + 45678), new Integer(12345)), + diff.TotalSupplyDiffs[USD.Hash]); + Assert.Null(diff.ValidatorSetDiff); // Note ValidatorSet is not tracked + } + + public IActionContext CreateActionContext(Address signer, ITrie trie) => + new ActionContext( + signer, + null, + signer, + 0, + Block.CurrentProtocolVersion, + new Account(new AccountState(trie)), + 0, + 0, + false); + + public ITrie Commit( + IStateStore stateStore, + ITrie baseTrie, + IAccountDelta accountDelta) + { + var trie = baseTrie; + var rawDelta = accountDelta.ToRawDelta(); + + foreach (var kv in rawDelta) + { + trie = trie.Set(kv.Key, kv.Value); + } + + return stateStore.Commit(trie); + } + } +} From d19fdac843f77e2bf0593612ab26bd7ba6f50e51 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 15 Sep 2023 17:45:16 +0900 Subject: [PATCH 10/31] Typo fix --- Libplanet.Action/State/AccountDiff.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libplanet.Action/State/AccountDiff.cs b/Libplanet.Action/State/AccountDiff.cs index 00a228ce5e7..dc414d63ed2 100644 --- a/Libplanet.Action/State/AccountDiff.cs +++ b/Libplanet.Action/State/AccountDiff.cs @@ -18,14 +18,14 @@ namespace Libplanet.Action.State /// from 's perspective. Keep in mind of the following properties: /// /// - /// Any value, which is equivalent to non-existant value in + /// Any value, which is equivalent to non-existent value in /// the underlying storage, in the source is ignored. That is, even if /// the value in the target and the value in the source are different while /// the value in the source is , this will not be /// part of the resulting . /// /// - /// Any value, again, which is equivalent to non-existant value + /// Any value, again, which is equivalent to non-existent value /// in the underlying storage, in the target for /// and is interpreted accordingly. That is, /// 0 amount of and empty From 8ab83ad6344629d006d1065cd659b33a862d19f6 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 19 Sep 2023 10:52:59 +0900 Subject: [PATCH 11/31] Changelog --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 8b9e5a89637..9c893e102ed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,6 +25,7 @@ To be released. ### Added APIs + - Added `AccountDiff` class. [[#3424]] - Added `IActionRenderContext` interface. [[#3427]] ### Behavioral changes @@ -35,6 +36,7 @@ To be released. ### CLI tools +[#3424]: https://github.com/planetarium/libplanet/pull/3424 [#3425]: https://github.com/planetarium/libplanet/pull/3425 [#3427]: https://github.com/planetarium/libplanet/pull/3427 From cf1d2d6f6b28ddb4ac12c0daaba97d62c26c2528 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 07:39:29 +0900 Subject: [PATCH 12/31] Removed unnecessary ISerializable implementation --- Libplanet.Tests/Tx/TxFailureTest.cs | 18 ------- Libplanet.Tests/Tx/TxSuccessTest.cs | 18 ------- Libplanet.Types/Tx/TxExecution.cs | 20 +------- Libplanet.Types/Tx/TxFailure.cs | 19 -------- Libplanet.Types/Tx/TxSuccess.cs | 75 +---------------------------- 5 files changed, 2 insertions(+), 148 deletions(-) diff --git a/Libplanet.Tests/Tx/TxFailureTest.cs b/Libplanet.Tests/Tx/TxFailureTest.cs index 0caebf920e5..f628e8054a6 100644 --- a/Libplanet.Tests/Tx/TxFailureTest.cs +++ b/Libplanet.Tests/Tx/TxFailureTest.cs @@ -1,7 +1,5 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Xunit; @@ -54,21 +52,5 @@ public void Constructor() Assert.Equal(nameof(ArgumentNullException), f.ExceptionName); Assert.Null(f.ExceptionMetadata); } - - [Fact] - public void Serialization() - { - var formatter = new BinaryFormatter(); - var stream = new MemoryStream(); - formatter.Serialize(stream, _fx); - stream.Seek(0, SeekOrigin.Begin); - object deserialized = formatter.Deserialize(stream); - Assert.IsType(deserialized); - var f = (TxFailure)deserialized; - Assert.Equal(_blockHash, f.BlockHash); - Assert.Equal(_txid, f.TxId); - Assert.Equal(_fx.ExceptionName, f.ExceptionName); - Assert.Equal(_fx.ExceptionMetadata, f.ExceptionMetadata); - } } } diff --git a/Libplanet.Tests/Tx/TxSuccessTest.cs b/Libplanet.Tests/Tx/TxSuccessTest.cs index 6418da8edbe..fecce72eece 100644 --- a/Libplanet.Tests/Tx/TxSuccessTest.cs +++ b/Libplanet.Tests/Tx/TxSuccessTest.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Immutable; -using System.IO; using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -71,21 +69,5 @@ public void ConstructorBuilder() Assert.Equal(_updatedStates, _fx.UpdatedStates); Assert.Equal(_updatedFungibleAssets, _fx.UpdatedFungibleAssets); } - - [Fact] - public void Serialization() - { - var formatter = new BinaryFormatter(); - var stream = new MemoryStream(); - formatter.Serialize(stream, _fx); - stream.Seek(0, SeekOrigin.Begin); - object deserialized = formatter.Deserialize(stream); - Assert.IsType(deserialized); - var s = (TxSuccess)deserialized; - Assert.Equal(_blockHash, s.BlockHash); - Assert.Equal(_txid, s.TxId); - Assert.Equal(_fx.UpdatedStates, s.UpdatedStates); - Assert.Equal(_fx.UpdatedFungibleAssets, s.UpdatedFungibleAssets); - } } } diff --git a/Libplanet.Types/Tx/TxExecution.cs b/Libplanet.Types/Tx/TxExecution.cs index c7ed50ba23b..c534c63b307 100644 --- a/Libplanet.Types/Tx/TxExecution.cs +++ b/Libplanet.Types/Tx/TxExecution.cs @@ -1,8 +1,5 @@ -using System; using System.Diagnostics.Contracts; -using System.Runtime.Serialization; using Bencodex; -using Libplanet.Common.Serialization; using Libplanet.Types.Blocks; namespace Libplanet.Types.Tx @@ -15,18 +12,10 @@ namespace Libplanet.Types.Tx /// /// /// - [Serializable] - public abstract class TxExecution : ISerializable + public abstract class TxExecution { protected static readonly Codec _codec = new Codec(); - protected TxExecution(SerializationInfo info, StreamingContext context) - : this( - info.GetValue(nameof(BlockHash)), - info.GetValue(nameof(TxId))) - { - } - private protected TxExecution(BlockHash blockHash, TxId txId) { BlockHash = blockHash; @@ -45,12 +34,5 @@ private protected TxExecution(BlockHash blockHash, TxId txId) /// [Pure] public TxId TxId { get; } - - /// - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue(nameof(BlockHash), BlockHash); - info.AddValue(nameof(TxId), TxId); - } } } diff --git a/Libplanet.Types/Tx/TxFailure.cs b/Libplanet.Types/Tx/TxFailure.cs index f15e4c744e1..707aa88c992 100644 --- a/Libplanet.Types/Tx/TxFailure.cs +++ b/Libplanet.Types/Tx/TxFailure.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.Contracts; -using System.Runtime.Serialization; using Bencodex.Types; using Libplanet.Types.Blocks; @@ -10,7 +9,6 @@ namespace Libplanet.Types.Tx /// Summarizes an execution result of a with any exception-throwing /// actions. /// - [Serializable] public sealed class TxFailure : TxExecution { /// @@ -54,12 +52,6 @@ public TxFailure( { } - private TxFailure(SerializationInfo info, StreamingContext context) - : base(info, context) - { - ExceptionName = info.GetString(nameof(ExceptionName)) ?? string.Empty; - } - /// /// The name of the exception type, e.g., System.ArgumentException. /// @@ -71,16 +63,5 @@ private TxFailure(SerializationInfo info, StreamingContext context) /// [Pure] public IValue? ExceptionMetadata => null; - - /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue(nameof(ExceptionName), ExceptionName); - info.AddValue( - nameof(ExceptionMetadata), - ExceptionMetadata is { } m ? _codec.Encode(m) : null - ); - } } } diff --git a/Libplanet.Types/Tx/TxSuccess.cs b/Libplanet.Types/Tx/TxSuccess.cs index e35bd0c1382..25aed81870e 100644 --- a/Libplanet.Types/Tx/TxSuccess.cs +++ b/Libplanet.Types/Tx/TxSuccess.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; -using System.Linq; -using System.Numerics; -using System.Runtime.Serialization; using Bencodex.Types; -using Libplanet.Common.Serialization; using Libplanet.Crypto; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; @@ -17,8 +11,7 @@ namespace Libplanet.Types.Tx /// /// Summarizes an execution result of a successful . /// - [Serializable] - public sealed class TxSuccess : TxExecution, ISerializable + public sealed class TxSuccess : TxExecution { /// /// Creates a instance. @@ -50,20 +43,6 @@ public TxSuccess( UpdatedFungibleAssets = updatedFungibleAssets; } - private TxSuccess(SerializationInfo info, StreamingContext context) - : base(info, context) - { - var updatedStates = - (Dictionary)_codec.Decode(info.GetValue(nameof(UpdatedStates))); - UpdatedStates = updatedStates.ToImmutableDictionary( - kv => new Address(kv.Key), - kv => kv.Value - ); - UpdatedFungibleAssets = DecodeFungibleAssetGroups( - info.GetValue(nameof(UpdatedFungibleAssets)) - ); - } - /// /// The states delta made by the actions in the transaction within the block. /// @@ -87,57 +66,5 @@ public IImmutableDictionary> [Pure] public IImmutableSet
UpdatedAddresses => UpdatedStates.Keys.ToImmutableHashSet().Union(UpdatedFungibleAssets.Keys); - - /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue( - nameof(UpdatedStates), - _codec.Encode( - new Dictionary( - UpdatedStates.Select(kv => - new KeyValuePair( - new Binary(kv.Key.ToByteArray()), - kv.Value)))) - ); - info.AddValue( - nameof(UpdatedFungibleAssets), - EncodeFungibleAssetGroups(UpdatedFungibleAssets) - ); - } - - private static byte[] EncodeFungibleAssetGroups( - IImmutableDictionary> g - ) => - _codec.Encode( - new Dictionary( - g.Select(kv => - new KeyValuePair( - kv.Key.ByteArray, - new List( - kv.Value.Select(fav => - List.Empty - .Add(fav.Key.Serialize()) - .Add(fav.Value.RawValue) - ) - ) - ) - ) - ) - ); - - private static IImmutableDictionary> - DecodeFungibleAssetGroups(byte[] encoded) => - ((Dictionary)_codec.Decode(encoded)).ToImmutableDictionary( - kv => new Address(kv.Key), - kv => (IImmutableDictionary)((List)kv.Value) - .Cast() - .Select(pair => (new Currency(pair[0]), (Bencodex.Types.Integer)pair[1])) - .ToImmutableDictionary( - pair => pair.Item1, - pair => FungibleAssetValue.FromRawValue(pair.Item1, (BigInteger)pair.Item2) - ) - ); } } From 0da3bbe10a2c284aa7382f86b28d22db1694f779 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 07:42:48 +0900 Subject: [PATCH 13/31] Remove unused ExceptionMetadata from TxFailure --- Libplanet.Store/BaseStore.cs | 2 +- Libplanet.Tests/Blockchain/BlockChainTest.Append.cs | 1 - Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs | 1 - Libplanet.Tests/Store/StoreTest.cs | 1 - Libplanet.Tests/Tx/TxFailureTest.cs | 2 -- Libplanet.Types/Tx/TxFailure.cs | 7 ------- 6 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Libplanet.Store/BaseStore.cs b/Libplanet.Store/BaseStore.cs index c2b7146154a..d6ac2b1873e 100644 --- a/Libplanet.Store/BaseStore.cs +++ b/Libplanet.Store/BaseStore.cs @@ -203,7 +203,7 @@ protected static IValue SerializeTxExecution(TxFailure txFailure) .Add("fail", true) .Add("exc", txFailure.ExceptionName); - return txFailure.ExceptionMetadata is { } v ? d.Add("excMeta", v) : d; + return d; } protected static TxExecution DeserializeTxExecution( diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 30ffcf280df..c157f939948 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -240,7 +240,6 @@ Func getTxExecution $"{nameof(System)}.{nameof(ArgumentOutOfRangeException)}", txFailure.ExceptionName ); - Assert.Null(txFailure.ExceptionMetadata); var txExecution3 = getTxExecution(block3.Hash, tx3Transfer.Id); _logger.Verbose(nameof(txExecution3) + " = {@TxExecution}", txExecution3); Assert.IsType(txExecution3); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs index c4b96ce5638..edc36f6275a 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs @@ -161,7 +161,6 @@ void AssertTxFailuresEqual(TxFailure expected, TxExecution actual) Assert.Equal(expected.TxId, failure.TxId); Assert.Equal(expected.BlockHash, failure.BlockHash); Assert.Equal(expected.ExceptionName, failure.ExceptionName); - Assert.Equal(expected.ExceptionMetadata, failure.ExceptionMetadata); } Func getTxExecution diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index 38a4dab3f64..8c464c83c74 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -410,7 +410,6 @@ void AssertTxFailuresEqual(TxFailure expected, TxExecution actual) Assert.Equal(expected.TxId, failure.TxId); Assert.Equal(expected.BlockHash, failure.BlockHash); Assert.Equal(expected.ExceptionName, failure.ExceptionName); - Assert.Equal(expected.ExceptionMetadata, failure.ExceptionMetadata); } Assert.Null(Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); diff --git a/Libplanet.Tests/Tx/TxFailureTest.cs b/Libplanet.Tests/Tx/TxFailureTest.cs index f628e8054a6..6897580aa97 100644 --- a/Libplanet.Tests/Tx/TxFailureTest.cs +++ b/Libplanet.Tests/Tx/TxFailureTest.cs @@ -36,7 +36,6 @@ public void ConstructorWithExceptionObject() Assert.Equal( $"{nameof(System)}.{nameof(ArgumentNullException)}", _fx.ExceptionName); - Assert.Null(_fx.ExceptionMetadata); } [Fact] @@ -50,7 +49,6 @@ public void Constructor() Assert.Equal(_blockHash, f.BlockHash); Assert.Equal(_txid, f.TxId); Assert.Equal(nameof(ArgumentNullException), f.ExceptionName); - Assert.Null(f.ExceptionMetadata); } } } diff --git a/Libplanet.Types/Tx/TxFailure.cs b/Libplanet.Types/Tx/TxFailure.cs index 707aa88c992..43ed9646293 100644 --- a/Libplanet.Types/Tx/TxFailure.cs +++ b/Libplanet.Types/Tx/TxFailure.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.Contracts; -using Bencodex.Types; using Libplanet.Types.Blocks; namespace Libplanet.Types.Tx @@ -57,11 +56,5 @@ public TxFailure( ///
[Pure] public string ExceptionName { get; } - - /// - /// Optional metadata about the exception. - /// - [Pure] - public IValue? ExceptionMetadata => null; } } From fa6bb26a52386a8c69a3f80449580bd8923592d6 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 09:31:37 +0900 Subject: [PATCH 14/31] Changelog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9c893e102ed..710a446002a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,9 @@ To be released. - Changed `IActionRenderer.RenderActionError(IValue, IActionContext, Exception)` to `IActionRenderer.RenderActionError(IValue, IActionRenderContext, Exception)`. [[#3427]] + - Removed `TxFailure.ExceptionMetadata` property. [[#3428]] + - Removed `ISerializable` interface from `TxExecution`, `TxSuccess`, + and `TxFailure`. [[#3428]] ### Backward-incompatible network protocol changes @@ -39,6 +42,7 @@ To be released. [#3424]: https://github.com/planetarium/libplanet/pull/3424 [#3425]: https://github.com/planetarium/libplanet/pull/3425 [#3427]: https://github.com/planetarium/libplanet/pull/3427 +[#3428]: https://github.com/planetarium/libplanet/pull/3428 Version 3.3.1 From 6b4ce880208ead54b052b11187b435ccdc64e3a0 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 05:17:01 +0900 Subject: [PATCH 15/31] Initial MakeTxExecutions overhaul --- .../Blockchain/BlockChain.TxExecution.cs | 106 ++++++++++++------ 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index 8b85b895eaa..9004bb98698 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -5,6 +6,7 @@ using System.Linq; using Libplanet.Action; using Libplanet.Action.State; +using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -28,44 +30,83 @@ IReadOnlyList evaluations .Where(e => e.InputContext.TxId is { }) .GroupBy(e => e.InputContext.TxId!.Value); int count = 0; - foreach (IGrouping txEvals in evaluationsPerTxs) + + List<(TxId?, List)> groupedEvals = + new List<(TxId?, List)>(); + foreach (IActionEvaluation eval in evaluations) { - TxId txid = txEvals.Key; - IAccount prevStates = txEvals.First().InputContext.PreviousState; - IActionEvaluation evalSum = txEvals.Last(); - TxExecution txExecution; - if (evalSum.Exception is { } e) + if (groupedEvals.Count == 0) { - txExecution = new TxFailure( - block.Hash, - txid, - e.InnerException ?? e); + groupedEvals.Add( + (eval.InputContext.TxId, new List() { eval })); } else { - IAccount outputStates = evalSum.OutputState; - txExecution = new TxSuccess( - block.Hash, - txid, - outputStates.GetUpdatedStates(), - outputStates.Delta.UpdatedFungibleAssets - .Select(pair => - ( - pair.Item1, - pair.Item2, - outputStates.GetBalance(pair.Item1, pair.Item2) - )) - .GroupBy(triple => triple.Item1) - .ToImmutableDictionary( - group => group.Key, - group => (IImmutableDictionary)group - .ToImmutableDictionary( - triple => triple.Item2, - triple => triple.Item3))); + if (groupedEvals.Last().Item1.Equals(eval.InputContext.TxId)) + { + groupedEvals.Last().Item2.Add(eval); + } + else + { + groupedEvals.Add( + (eval.InputContext.TxId, new List() { eval })); + } } + } - yield return txExecution; - count++; + ITrie trie = GetAccountState(block.PreviousHash).Trie; + + foreach (var group in groupedEvals) + { + if (group.Item1 is { } txId) + { + // make tx execution + ITrie nextTrie = trie; + foreach (var eval in group.Item2) + { + foreach (var kv in eval.OutputState.Delta.ToRawDelta()) + { + nextTrie = nextTrie.Set(kv.Key, kv.Value); + } + } + + nextTrie = StateStore.Commit(nextTrie); + + List exceptions = group.Item2 + .Select(eval => eval.Exception) + .ToList(); + + yield return exceptions.Any(exception => exception is { }) + ? new TxFailure( + block.Hash, + txId, + trie.Hash, + nextTrie.Hash, + exceptions) + : new TxSuccess( + block.Hash, + txId, + trie.Hash, + nextTrie.Hash, + exceptions); + count++; + trie = nextTrie; + } + else + { + // move forward + ITrie nextTrie = trie; + foreach (var eval in group.Item2) + { + foreach (var kv in eval.OutputState.Delta.ToRawDelta()) + { + nextTrie = nextTrie.Set(kv.Key, kv.Value); + } + } + + nextTrie = StateStore.Commit(nextTrie); + trie = nextTrie; + } } _logger.Verbose( @@ -73,8 +114,7 @@ IReadOnlyList evaluations "s for {Txs} transactions within the block #{BlockIndex} {BlockHash}", count, block.Index, - block.Hash - ); + block.Hash); } internal void UpdateTxExecutions(IEnumerable txExecutions) From 3b1ba6b27ab52888093b248770b1e2c3513cf6c9 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 11:42:38 +0900 Subject: [PATCH 16/31] Reimplement TxSuccess and TxFailure --- Libplanet.Types/Tx/TxFailure.cs | 55 +++++++------ Libplanet.Types/Tx/TxSuccess.cs | 82 +++++++++---------- .../Blockchain/BlockChain.TxExecution.cs | 24 +++--- 3 files changed, 81 insertions(+), 80 deletions(-) diff --git a/Libplanet.Types/Tx/TxFailure.cs b/Libplanet.Types/Tx/TxFailure.cs index 43ed9646293..8427980b99a 100644 --- a/Libplanet.Types/Tx/TxFailure.cs +++ b/Libplanet.Types/Tx/TxFailure.cs @@ -1,5 +1,8 @@ using System; -using System.Diagnostics.Contracts; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Libplanet.Common; using Libplanet.Types.Blocks; namespace Libplanet.Types.Tx @@ -17,44 +20,48 @@ public sealed class TxFailure : TxExecution /// that the is executed within. /// The executed 's . - /// The name of the exception type, - /// e.g., System.ArgumentException. - /// Optional metadata about the exception. + /// The state root hash of the state before the + /// execution of a . + /// The state root hash of the state after the + /// execution of a . + /// The list of excetions thrown while + /// executing the actions. public TxFailure( BlockHash blockHash, TxId txId, - string exceptionName - ) + HashDigest inputState, + HashDigest outputState, + List exceptionNames) : base(blockHash, txId) { - ExceptionName = exceptionName; + InputState = inputState; + OutputState = outputState; + ExceptionNames = exceptionNames; } - /// - /// Creates a instance. - /// - /// The of the - /// that the is executed within. - /// The executed 's . - /// The uncaught exception thrown by an action in the transaction. - /// public TxFailure( BlockHash blockHash, TxId txId, - Exception exception) + HashDigest inputState, + HashDigest outputState, + List exceptions) : this( blockHash, txId, - exception.GetType().FullName ?? string.Empty - ) + inputState, + outputState, + exceptions + .Select(exception => exception is { } e + ? e.GetType().FullName + : string.Empty) + .ToList()) { } - /// - /// The name of the exception type, e.g., System.ArgumentException. - /// - [Pure] - public string ExceptionName { get; } + public HashDigest InputState { get; } + + public HashDigest OutputState { get; } + + public List ExceptionNames { get; } } } diff --git a/Libplanet.Types/Tx/TxSuccess.cs b/Libplanet.Types/Tx/TxSuccess.cs index 25aed81870e..a41e6fc034b 100644 --- a/Libplanet.Types/Tx/TxSuccess.cs +++ b/Libplanet.Types/Tx/TxSuccess.cs @@ -1,10 +1,9 @@ -using System.Collections.Immutable; -using System.Diagnostics.Contracts; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Libplanet.Common; using Libplanet.Types.Blocks; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; namespace Libplanet.Types.Tx { @@ -20,51 +19,48 @@ public sealed class TxSuccess : TxExecution /// that the is executed within. /// The executed 's . - /// The states delta made by the actions in - /// the transaction within the block. - /// es and sets of - /// whose fungible assets have been updated by the actions in - /// the transaction within the block. Included s are - /// the delta values (plus or minus) that the transaction makes. - /// es and sets of - /// whose fungible assets have been updated by the actions in - /// the transaction within the block. Included s are - /// the final balances right after the transaction is executed. + /// The state root hash of the state before the + /// execution of a . + /// The state root hash of the state after the + /// execution of a . + /// The list of excetions thrown while + /// executing the actions. public TxSuccess( BlockHash blockHash, TxId txId, - IImmutableDictionary updatedStates, - IImmutableDictionary> - updatedFungibleAssets - ) + HashDigest inputState, + HashDigest outputState, + List exceptionNames) : base(blockHash, txId) { - UpdatedStates = updatedStates; - UpdatedFungibleAssets = updatedFungibleAssets; + InputState = inputState; + OutputState = outputState; + ExceptionNames = exceptionNames; } - /// - /// The states delta made by the actions in the transaction within the block. - /// - [Pure] - public IImmutableDictionary UpdatedStates { get; } + public TxSuccess( + BlockHash blockHash, + TxId txId, + HashDigest inputState, + HashDigest outputState, + List exceptions) + : this( + blockHash, + txId, + inputState, + outputState, + exceptions + .Select(exception => exception is { } e + ? e.GetType().FullName + : string.Empty) + .ToList()) + { + } - /// - /// es and sets of whose fungible assets have - /// been updated by the actions in the transaction within the block. Included - /// s are the final balances right after the transaction is - /// executed. - /// - [Pure] - public IImmutableDictionary> - UpdatedFungibleAssets { get; } + public HashDigest InputState { get; } - /// - /// All es of the accounts that have been updated by the actions - /// in the transaction within the block. - /// - [Pure] - public IImmutableSet
UpdatedAddresses => - UpdatedStates.Keys.ToImmutableHashSet().Union(UpdatedFungibleAssets.Keys); + public HashDigest OutputState { get; } + + public List ExceptionNames { get; } } } diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index 9004bb98698..e0a8b72c061 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.Linq; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Store.Trie; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -26,11 +24,6 @@ internal IEnumerable MakeTxExecutions( IReadOnlyList evaluations ) { - IEnumerable> evaluationsPerTxs = evaluations - .Where(e => e.InputContext.TxId is { }) - .GroupBy(e => e.InputContext.TxId!.Value); - int count = 0; - List<(TxId?, List)> groupedEvals = new List<(TxId?, List)>(); foreach (IActionEvaluation eval in evaluations) @@ -56,11 +49,11 @@ IReadOnlyList evaluations ITrie trie = GetAccountState(block.PreviousHash).Trie; + int count = 0; foreach (var group in groupedEvals) { if (group.Item1 is { } txId) { - // make tx execution ITrie nextTrie = trie; foreach (var eval in group.Item2) { @@ -76,25 +69,30 @@ IReadOnlyList evaluations .Select(eval => eval.Exception) .ToList(); - yield return exceptions.Any(exception => exception is { }) - ? new TxFailure( + if (exceptions.Any(exception => exception is { })) + { + yield return new TxFailure( block.Hash, txId, trie.Hash, nextTrie.Hash, - exceptions) - : new TxSuccess( + exceptions); + } + else + { + yield return new TxSuccess( block.Hash, txId, trie.Hash, nextTrie.Hash, exceptions); + } + count++; trie = nextTrie; } else { - // move forward ITrie nextTrie = trie; foreach (var eval in group.Item2) { From 83df0c7c4539b0a67b7a25e17dba170a9dc926b7 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 17:47:25 +0900 Subject: [PATCH 17/31] Combine TxSuccess and TxFailure --- Libplanet.Types/Tx/TxExecution.cs | 150 ++++++++++++++++++++++++++++-- Libplanet.Types/Tx/TxFailure.cs | 67 ------------- Libplanet.Types/Tx/TxSuccess.cs | 66 ------------- 3 files changed, 144 insertions(+), 139 deletions(-) delete mode 100644 Libplanet.Types/Tx/TxFailure.cs delete mode 100644 Libplanet.Types/Tx/TxSuccess.cs diff --git a/Libplanet.Types/Tx/TxExecution.cs b/Libplanet.Types/Tx/TxExecution.cs index c534c63b307..4fb13998c70 100644 --- a/Libplanet.Types/Tx/TxExecution.cs +++ b/Libplanet.Types/Tx/TxExecution.cs @@ -1,5 +1,10 @@ +using System; +using System.Collections.Generic; using System.Diagnostics.Contracts; -using Bencodex; +using System.Linq; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Common; using Libplanet.Types.Blocks; namespace Libplanet.Types.Tx @@ -10,16 +15,116 @@ namespace Libplanet.Types.Tx /// a , and even if it's the same its /// result can vary depending on that it is executed within. ///
- /// - /// - public abstract class TxExecution + public sealed class TxExecution { - protected static readonly Codec _codec = new Codec(); + internal static readonly Text FailKey = new Text("fail"); - private protected TxExecution(BlockHash blockHash, TxId txId) + internal static readonly Text InputStateKey = new Text("i"); + + internal static readonly Text OutputStateKey = new Text("o"); + + internal static readonly Text ExceptionNamesKey = new Text("e"); + + public TxExecution( + BlockHash blockHash, + TxId txId, + bool fail, + HashDigest inputState, + HashDigest outputState, + List exceptions) + : this( + blockHash, + txId, + fail, + inputState, + outputState, + exceptions + .Select(exception => exception is { } e + ? e.GetType().FullName + : string.Empty) + .ToList()) + { + } + + public TxExecution( + BlockHash blockHash, + TxId txId, + bool fail, + HashDigest inputState, + HashDigest outputState, + List exceptionNames) { BlockHash = blockHash; TxId = txId; + Fail = fail; + InputState = inputState; + OutputState = outputState; + ExceptionNames = exceptionNames; + } + +#pragma warning disable SA1118 // The parameter spans multiple lines. + public TxExecution( + BlockHash blockHash, + TxId txId, + IValue encoded) + : this( + blockHash, + txId, + encoded is Dictionary dict + ? dict + : throw new ArgumentException()) +#pragma warning restore SA1118 + { + } + + private TxExecution( + BlockHash blockHash, + TxId txId, + Dictionary encoded) + { + BlockHash = blockHash; + TxId = txId; + + if (!encoded.TryGetValue(FailKey, out IValue fail) || + !(fail is Bencodex.Types.Boolean failBoolean) || + (failBoolean.Value == false)) + { + throw new ArgumentException($"Invalid fail key value: {fail}"); + } + + if (encoded.TryGetValue(InputStateKey, out IValue input) && + input is Binary inputBinary) + { + InputState = HashDigest.DeriveFrom(inputBinary.ByteArray); + } + else + { + InputState = null; + } + + if (encoded.TryGetValue(OutputStateKey, out IValue output) && + input is Binary outputBinary) + { + InputState = HashDigest.DeriveFrom(outputBinary.ByteArray); + } + else + { + OutputState = null; + } + + if (encoded.TryGetValue(ExceptionNamesKey, out IValue exceptions) && + exceptions is List exceptionsList) + { + ExceptionNames = exceptionsList + .Select(value => value is Text t + ? t.Value + : throw new ArgumentException()) + .ToList(); + } + else + { + ExceptionNames = null; + } } /// @@ -34,5 +139,38 @@ private protected TxExecution(BlockHash blockHash, TxId txId) /// [Pure] public TxId TxId { get; } + + public bool Fail { get; } + + public HashDigest? InputState { get; } + + public HashDigest? OutputState { get; } + + public List? ExceptionNames { get; } + + public IValue ToBencodex() + { + Dictionary dict = Dictionary.Empty + .Add(FailKey, Fail); + + if (InputState is { } inputState) + { + dict = dict.Add(InputStateKey, inputState.ByteArray); + } + + if (OutputState is { } outputState) + { + dict = dict.Add(OutputStateKey, outputState.ByteArray); + } + + if (ExceptionNames is { } exceptionNames) + { + dict = dict.Add( + ExceptionNamesKey, + new List(exceptionNames.Select(exceptionName => new Text(exceptionName)))); + } + + return dict; + } } } diff --git a/Libplanet.Types/Tx/TxFailure.cs b/Libplanet.Types/Tx/TxFailure.cs deleted file mode 100644 index 8427980b99a..00000000000 --- a/Libplanet.Types/Tx/TxFailure.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Libplanet.Common; -using Libplanet.Types.Blocks; - -namespace Libplanet.Types.Tx -{ - /// - /// Summarizes an execution result of a with any exception-throwing - /// actions. - /// - public sealed class TxFailure : TxExecution - { - /// - /// Creates a instance. - /// - /// The of the - /// that the is executed within. - /// The executed 's . - /// The state root hash of the state before the - /// execution of a . - /// The state root hash of the state after the - /// execution of a . - /// The list of excetions thrown while - /// executing the actions. - public TxFailure( - BlockHash blockHash, - TxId txId, - HashDigest inputState, - HashDigest outputState, - List exceptionNames) - : base(blockHash, txId) - { - InputState = inputState; - OutputState = outputState; - ExceptionNames = exceptionNames; - } - - public TxFailure( - BlockHash blockHash, - TxId txId, - HashDigest inputState, - HashDigest outputState, - List exceptions) - : this( - blockHash, - txId, - inputState, - outputState, - exceptions - .Select(exception => exception is { } e - ? e.GetType().FullName - : string.Empty) - .ToList()) - { - } - - public HashDigest InputState { get; } - - public HashDigest OutputState { get; } - - public List ExceptionNames { get; } - } -} diff --git a/Libplanet.Types/Tx/TxSuccess.cs b/Libplanet.Types/Tx/TxSuccess.cs deleted file mode 100644 index a41e6fc034b..00000000000 --- a/Libplanet.Types/Tx/TxSuccess.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Libplanet.Common; -using Libplanet.Types.Blocks; - -namespace Libplanet.Types.Tx -{ - /// - /// Summarizes an execution result of a successful . - /// - public sealed class TxSuccess : TxExecution - { - /// - /// Creates a instance. - /// - /// The of the - /// that the is executed within. - /// The executed 's . - /// The state root hash of the state before the - /// execution of a . - /// The state root hash of the state after the - /// execution of a . - /// The list of excetions thrown while - /// executing the actions. - public TxSuccess( - BlockHash blockHash, - TxId txId, - HashDigest inputState, - HashDigest outputState, - List exceptionNames) - : base(blockHash, txId) - { - InputState = inputState; - OutputState = outputState; - ExceptionNames = exceptionNames; - } - - public TxSuccess( - BlockHash blockHash, - TxId txId, - HashDigest inputState, - HashDigest outputState, - List exceptions) - : this( - blockHash, - txId, - inputState, - outputState, - exceptions - .Select(exception => exception is { } e - ? e.GetType().FullName - : string.Empty) - .ToList()) - { - } - - public HashDigest InputState { get; } - - public HashDigest OutputState { get; } - - public List ExceptionNames { get; } - } -} From b58f95efd2820c146eb4dce495afa657a52a059d Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 18:15:58 +0900 Subject: [PATCH 18/31] Update IStore interface and its implementations --- Libplanet.Explorer/Store/LiteDBRichStore.cs | 10 ++-- Libplanet.Explorer/Store/MySQLRichStore.cs | 10 ++-- Libplanet.RocksDBStore/RocksDBStore.cs | 15 ++---- Libplanet.Store/BaseStore.cs | 55 ++------------------- Libplanet.Store/DefaultStore.cs | 19 ++----- Libplanet.Store/IStore.cs | 21 ++------ Libplanet.Store/MemoryStore.cs | 7 +-- Libplanet.Tests/Store/ProxyStore.cs | 10 ++-- Libplanet.Tests/Store/StoreTracker.cs | 12 ++--- 9 files changed, 29 insertions(+), 130 deletions(-) diff --git a/Libplanet.Explorer/Store/LiteDBRichStore.cs b/Libplanet.Explorer/Store/LiteDBRichStore.cs index 9e666f7e795..f2001263837 100644 --- a/Libplanet.Explorer/Store/LiteDBRichStore.cs +++ b/Libplanet.Explorer/Store/LiteDBRichStore.cs @@ -90,13 +90,9 @@ public LiteDBRichStore( return _store.GetBlockIndex(blockHash); } - /// - public void PutTxExecution(TxSuccess txSuccess) => - _store.PutTxExecution(txSuccess); - - /// - public void PutTxExecution(TxFailure txFailure) => - _store.PutTxExecution(txFailure); + /// + public void PutTxExecution(TxExecution txExecution) => + _store.PutTxExecution(txExecution); /// public TxExecution GetTxExecution(BlockHash blockHash, TxId txid) => diff --git a/Libplanet.Explorer/Store/MySQLRichStore.cs b/Libplanet.Explorer/Store/MySQLRichStore.cs index a78d4c3eb87..e51cdf18555 100644 --- a/Libplanet.Explorer/Store/MySQLRichStore.cs +++ b/Libplanet.Explorer/Store/MySQLRichStore.cs @@ -53,13 +53,9 @@ public MySQLRichStore(IStore store, MySQLRichStoreOptions options) public long? GetBlockIndex(BlockHash blockHash) => _store.GetBlockIndex(blockHash); - /// - public void PutTxExecution(TxSuccess txSuccess) => - _store.PutTxExecution(txSuccess); - - /// - public void PutTxExecution(TxFailure txFailure) => - _store.PutTxExecution(txFailure); + /// + public void PutTxExecution(TxExecution txExecution) => + _store.PutTxExecution(txExecution); /// public TxExecution GetTxExecution(BlockHash blockHash, TxId txid) => diff --git a/Libplanet.RocksDBStore/RocksDBStore.cs b/Libplanet.RocksDBStore/RocksDBStore.cs index 533c15cee99..484fbade7dd 100644 --- a/Libplanet.RocksDBStore/RocksDBStore.cs +++ b/Libplanet.RocksDBStore/RocksDBStore.cs @@ -986,18 +986,11 @@ public override IEnumerable IterateTxIdBlockHashIndex(TxId txId) } } - /// - public override void PutTxExecution(TxSuccess txSuccess) => + /// + public override void PutTxExecution(TxExecution txExecution) => _txExecutionDb.Put( - TxExecutionKey(txSuccess), - Codec.Encode(SerializeTxExecution(txSuccess)) - ); - - /// - public override void PutTxExecution(TxFailure txFailure) => - _txExecutionDb.Put( - TxExecutionKey(txFailure), - Codec.Encode(SerializeTxExecution(txFailure)) + TxExecutionKey(txExecution), + Codec.Encode(SerializeTxExecution(txExecution)) ); /// diff --git a/Libplanet.Store/BaseStore.cs b/Libplanet.Store/BaseStore.cs index d6ac2b1873e..6faca3dca3e 100644 --- a/Libplanet.Store/BaseStore.cs +++ b/Libplanet.Store/BaseStore.cs @@ -99,10 +99,7 @@ public Block GetBlock(BlockHash blockHash) public abstract bool ContainsBlock(BlockHash blockHash); /// - public abstract void PutTxExecution(TxSuccess txSuccess); - - /// - public abstract void PutTxExecution(TxFailure txFailure); + public abstract void PutTxExecution(TxExecution txExecution); /// public abstract TxExecution GetTxExecution(BlockHash blockHash, TxId txid); @@ -178,32 +175,9 @@ public virtual long CountBlocks() /// public abstract IEnumerable GetBlockCommitHashes(); - protected static IValue SerializeTxExecution(TxSuccess txSuccess) - { - var sDelta = new Dictionary( - txSuccess.UpdatedStates.Select(kv => - new KeyValuePair( - new Binary(kv.Key.ByteArray), - kv.Value is { } v ? List.Empty.Add(v) : List.Empty - ) - ) - ); - var updatedFAVs = SerializeGroupedFAVs(txSuccess.UpdatedFungibleAssets); - var serialized = Dictionary.Empty - .Add("fail", false) - .Add("sDelta", sDelta) - .Add("updatedFAVs", new Dictionary(updatedFAVs)); - - return serialized; - } - - protected static IValue SerializeTxExecution(TxFailure txFailure) + protected static IValue SerializeTxExecution(TxExecution txExecution) { - Dictionary d = Dictionary.Empty - .Add("fail", true) - .Add("exc", txFailure.ExceptionName); - - return d; + return txExecution.ToBencodex(); } protected static TxExecution DeserializeTxExecution( @@ -223,28 +197,7 @@ ILogger logger try { - bool fail = d.GetValue("fail"); - - if (fail) - { - string excName = d.GetValue("exc"); - return new TxFailure(blockHash, txid, excName); - } - - ImmutableDictionary sDelta = d.GetValue("sDelta") - .ToImmutableDictionary( - kv => new Address((IValue)kv.Key), - kv => kv.Value is List l && l.Any() ? l[0] : null - ); - IImmutableDictionary> - updatedFAVs = DeserializeGroupedFAVs(d.GetValue("updatedFAVs")); - - return new TxSuccess( - blockHash, - txid, - sDelta, - updatedFAVs - ); + return new TxExecution(blockHash, txid, d); } catch (Exception e) { diff --git a/Libplanet.Store/DefaultStore.cs b/Libplanet.Store/DefaultStore.cs index f7b8c37846b..8a83a5002af 100644 --- a/Libplanet.Store/DefaultStore.cs +++ b/Libplanet.Store/DefaultStore.cs @@ -489,26 +489,15 @@ public override bool ContainsBlock(BlockHash blockHash) return _blocks.FileExists(blockPath); } - /// - public override void PutTxExecution(TxSuccess txSuccess) + /// + public override void PutTxExecution(TxExecution txExecution) { - UPath path = TxExecutionPath(txSuccess); + UPath path = TxExecutionPath(txExecution); UPath dirPath = path.GetDirectory(); CreateDirectoryRecursively(_txExecutions, dirPath); using Stream f = _txExecutions.OpenFile(path, System.IO.FileMode.Create, FileAccess.Write); - Codec.Encode(SerializeTxExecution(txSuccess), f); - } - - /// - public override void PutTxExecution(TxFailure txFailure) - { - UPath path = TxExecutionPath(txFailure); - UPath dirPath = path.GetDirectory(); - CreateDirectoryRecursively(_txExecutions, dirPath); - using Stream f = - _txExecutions.OpenFile(path, System.IO.FileMode.Create, FileAccess.Write); - Codec.Encode(SerializeTxExecution(txFailure), f); + Codec.Encode(SerializeTxExecution(txExecution), f); } /// diff --git a/Libplanet.Store/IStore.cs b/Libplanet.Store/IStore.cs index d84e802ca7f..a2d39af0f22 100644 --- a/Libplanet.Store/IStore.cs +++ b/Libplanet.Store/IStore.cs @@ -166,26 +166,12 @@ public interface IStore : IDisposable ///
/// If there is already the record for the same /// and , the record is silently overwritten. - /// The successful transaction execution summary to record. + /// The transaction execution summary to record. /// Must not be . /// Thrown when is /// . - /// /// - void PutTxExecution(TxSuccess txSuccess); - - /// - /// Records the given . - /// - /// If there is already the record for the same - /// and , the record is silently overwritten. - /// The failed transaction execution summary to record. - /// Must not be . - /// Thrown when is - /// . - /// - /// - void PutTxExecution(TxFailure txFailure); + void PutTxExecution(TxExecution txExecution); /// /// Retrieves the recorded transaction execution summary. @@ -196,8 +182,7 @@ public interface IStore : IDisposable /// execution to retrieve. /// The recorded transaction execution summary. If it has been never recorded /// is returned instead. - /// - /// + /// TxExecution GetTxExecution(BlockHash blockHash, TxId txid); /// diff --git a/Libplanet.Store/MemoryStore.cs b/Libplanet.Store/MemoryStore.cs index 091747b3977..755f5c4733b 100644 --- a/Libplanet.Store/MemoryStore.cs +++ b/Libplanet.Store/MemoryStore.cs @@ -186,11 +186,8 @@ bool IStore.DeleteBlock(BlockHash blockHash) => bool IStore.ContainsBlock(BlockHash blockHash) => _blocks.ContainsKey(blockHash); - void IStore.PutTxExecution(TxSuccess txSuccess) => - _txExecutions[(txSuccess.BlockHash, txSuccess.TxId)] = txSuccess; - - void IStore.PutTxExecution(TxFailure txFailure) => - _txExecutions[(txFailure.BlockHash, txFailure.TxId)] = txFailure; + void IStore.PutTxExecution(TxExecution txExecution) => + _txExecutions[(txExecution.BlockHash, txExecution.TxId)] = txExecution; TxExecution IStore.GetTxExecution(BlockHash blockHash, TxId txid) => _txExecutions.TryGetValue((blockHash, txid), out TxExecution e) ? e : null; diff --git a/Libplanet.Tests/Store/ProxyStore.cs b/Libplanet.Tests/Store/ProxyStore.cs index 6325b207b02..dde51f23d54 100644 --- a/Libplanet.Tests/Store/ProxyStore.cs +++ b/Libplanet.Tests/Store/ProxyStore.cs @@ -114,13 +114,9 @@ public virtual bool DeleteBlock(BlockHash blockHash) => public virtual bool ContainsBlock(BlockHash blockHash) => Store.ContainsBlock(blockHash); - /// - public virtual void PutTxExecution(TxSuccess txSuccess) => - Store.PutTxExecution(txSuccess); - - /// - public virtual void PutTxExecution(TxFailure txFailure) => - Store.PutTxExecution(txFailure); + /// + public virtual void PutTxExecution(TxExecution txExecution) => + Store.PutTxExecution(txExecution); /// public virtual TxExecution GetTxExecution(BlockHash blockHash, TxId txid) => diff --git a/Libplanet.Tests/Store/StoreTracker.cs b/Libplanet.Tests/Store/StoreTracker.cs index 241a801b8d5..c2faff600b1 100644 --- a/Libplanet.Tests/Store/StoreTracker.cs +++ b/Libplanet.Tests/Store/StoreTracker.cs @@ -47,16 +47,10 @@ public bool ContainsBlock(BlockHash blockHash) return _store.ContainsBlock(blockHash); } - public void PutTxExecution(TxSuccess txSuccess) + public void PutTxExecution(TxExecution txExecution) { - Log(nameof(PutTxExecution), txSuccess); - _store.PutTxExecution(txSuccess); - } - - public void PutTxExecution(TxFailure txFailure) - { - Log(nameof(PutTxExecution), txFailure); - _store.PutTxExecution(txFailure); + Log(nameof(PutTxExecution), txExecution); + _store.PutTxExecution(txExecution); } public TxExecution GetTxExecution(BlockHash blockHash, TxId txid) From e3439ca82aafb7845a1055a1f621b527fd6426e0 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 18:26:54 +0900 Subject: [PATCH 19/31] Update TxExecution creation from BlockChain --- .../Blockchain/BlockChain.TxExecution.cs | 68 ++++--------------- 1 file changed, 13 insertions(+), 55 deletions(-) diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index e0a8b72c061..34eda701b2b 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; using System.Linq; using Libplanet.Action; using Libplanet.Action.State; @@ -69,24 +67,13 @@ IReadOnlyList evaluations .Select(eval => eval.Exception) .ToList(); - if (exceptions.Any(exception => exception is { })) - { - yield return new TxFailure( - block.Hash, - txId, - trie.Hash, - nextTrie.Hash, - exceptions); - } - else - { - yield return new TxSuccess( - block.Hash, - txId, - trie.Hash, - nextTrie.Hash, - exceptions); - } + yield return new TxExecution( + block.Hash, + txId, + exceptions.Any(exception => exception is { }), + trie.Hash, + nextTrie.Hash, + exceptions); count++; trie = nextTrie; @@ -120,42 +107,13 @@ internal void UpdateTxExecutions(IEnumerable txExecutions) int count = 0; foreach (TxExecution txExecution in txExecutions) { - // Note that there are two overloaded methods of the same name PutTxExecution() - // in IStore. As those two have different signatures, run-time polymorphism - // does not work. Instead, we need the following hard-coded branch: - switch (txExecution) - { - case TxSuccess s: - Store.PutTxExecution(s); // IStore.PutTxExecution(TxSuccess) - _logger.Verbose( - "Updated " + nameof(TxSuccess) + - " for tx {TxId} within block {BlockHash}", - s.TxId, - s.BlockHash - ); - break; - case TxFailure f: - Store.PutTxExecution(f); // IStore.PutTxExecution(TxFailure) - _logger.Verbose( - "Updated " + nameof(TxFailure) + - " for tx {TxId} within block {BlockHash}", - f.TxId, - f.BlockHash - ); - break; - default: - // In theory, this case must not happen. The following case is for just in - // case. (For example, we might add a new subtype for TxExecution.) - const string msg = "Unexpected subtype of " + nameof(TxExecution) + ": {0}"; - _logger.Fatal(msg, txExecution); - Trace.Assert( - false, - string.Format(CultureInfo.InvariantCulture, msg, txExecution) - ); - break; - } - + Store.PutTxExecution(txExecution); count++; + + _logger.Verbose( + "Updated " + nameof(TxExecution) + " for tx {TxId} within block {BlockHash}", + txExecution.TxId, + txExecution.BlockHash); } _logger.Verbose( From c44e0f066cb8d31acb5bab2b3c6558b67abd796b Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 18:29:21 +0900 Subject: [PATCH 20/31] Updated TransactionQuery for TxResult --- .../Queries/TransactionQuery.cs | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/Libplanet.Explorer/Queries/TransactionQuery.cs b/Libplanet.Explorer/Queries/TransactionQuery.cs index 88342431640..fa7f2f4b990 100644 --- a/Libplanet.Explorer/Queries/TransactionQuery.cs +++ b/Libplanet.Explorer/Queries/TransactionQuery.cs @@ -268,30 +268,13 @@ index is null ); var txExecutedBlock = blockChain[txExecutedBlockHashValue]; - return execution switch - { - TxSuccess txSuccess => new TxResult( - TxStatus.SUCCESS, - txExecutedBlock.Index, - txExecutedBlock.Hash.ToString(), - null, - txSuccess.UpdatedStates, - txSuccess.UpdatedFungibleAssets - ), - TxFailure txFailure => new TxResult( - TxStatus.FAILURE, - txExecutedBlock.Index, - txExecutedBlock.Hash.ToString(), - txFailure.ExceptionName, - null, - null - ), - _ => throw new NotSupportedException( - #pragma warning disable format - $"{nameof(execution)} is not expected concrete class." - #pragma warning restore format - ), - }; + return new TxResult( + execution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS, + txExecutedBlock.Index, + txExecutedBlock.Hash.ToString(), + null, + null, + null); } catch (Exception) { From e09838f6d3274416eb16b343d748594c509688fd Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 20 Sep 2023 21:00:25 +0900 Subject: [PATCH 21/31] Fix tests --- .../Blockchain/BlockChainTest.Append.cs | 108 +++++++------ .../Blockchain/BlockChainTest.Internals.cs | 68 ++++----- .../Blockchain/BlockChainTest.ProposeBlock.cs | 2 +- Libplanet.Tests/Store/StoreTest.cs | 74 ++++----- Libplanet.Tests/Tx/TxExecutionTest.cs | 144 ++++++++++++++++++ Libplanet.Tests/Tx/TxFailureTest.cs | 54 ------- Libplanet.Tests/Tx/TxSuccessTest.cs | 73 --------- Libplanet.Types/Tx/TxExecution.cs | 13 +- .../Blockchain/BlockChain.TxExecution.cs | 5 +- 9 files changed, 265 insertions(+), 276 deletions(-) create mode 100644 Libplanet.Tests/Tx/TxExecutionTest.cs delete mode 100644 Libplanet.Tests/Tx/TxFailureTest.cs delete mode 100644 Libplanet.Tests/Tx/TxSuccessTest.cs diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index c157f939948..f9aa4c2a87c 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -6,6 +6,7 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -15,7 +16,6 @@ using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Tests.Store; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Xunit; @@ -157,11 +157,15 @@ Func getTxExecution Assert.Null(getTxExecution(block1.Hash, tx.Id)); TxExecution e = getTxExecution(block2.Hash, tx.Id); - Assert.IsType(e); - var s = (TxSuccess)e; - Assert.Equal(block2.Hash, s.BlockHash); - Assert.Equal(tx.Id, s.TxId); - Assert.Empty(s.UpdatedFungibleAssets); + Assert.False(e.Fail); + Assert.Equal(block2.Hash, e.BlockHash); + Assert.Equal(tx.Id, e.TxId); + var inputAccount = + _blockChain.GetAccountState(Assert.IsType>(e.InputState)); + var outputAccount = + _blockChain.GetAccountState(Assert.IsType>(e.OutputState)); + var accountDiff = AccountDiff.Create(inputAccount, outputAccount); + Assert.Empty(accountDiff.FungibleAssetValueDiffs); } var pk = new PrivateKey(); @@ -199,65 +203,57 @@ Func getTxExecution _blockChain.Append(block3, TestUtils.CreateBlockCommit(block3)); var txExecution1 = getTxExecution(block3.Hash, tx1Transfer.Id); _logger.Verbose(nameof(txExecution1) + " = {@TxExecution}", txExecution1); - Assert.IsType(txExecution1); - var txSuccess1 = (TxSuccess)txExecution1; + Assert.False(txExecution1.Fail); + var inputAccount1 = _blockChain.GetAccountState( + Assert.IsType>(txExecution1.InputState)); + var outputAccount1 = _blockChain.GetAccountState( + Assert.IsType>(txExecution1.OutputState)); + var accountDiff1 = AccountDiff.Create(inputAccount1, outputAccount1); + Assert.Equal( - addresses.Take(3).Append(pk.ToAddress()).ToImmutableHashSet(), - txSuccess1.UpdatedAddresses - ); + (new Address[] { addresses[0], pk.ToAddress() }).ToImmutableHashSet(), + accountDiff1.StateDiffs.Select(kv => kv.Key).ToImmutableHashSet()); Assert.Equal( - ImmutableDictionary.Empty - .Add(pk.ToAddress(), (Text)"foo") - .Add(addresses[0], (Text)"foo,bar"), - txSuccess1.UpdatedStates - ); + (new Address[] { addresses[1], addresses[2], pk.ToAddress() }) + .ToImmutableHashSet(), + accountDiff1.FungibleAssetValueDiffs.Select(kv => kv.Key.Item1) + .ToImmutableHashSet()); Assert.Equal( - ImmutableDictionary>.Empty - .Add( - pk.ToAddress(), - ImmutableDictionary.Empty - .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * -30) - ) - .Add( - addresses[1], - ImmutableDictionary.Empty - .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 10) - ) - .Add( - addresses[2], - ImmutableDictionary.Empty - .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 20) - ), - txSuccess1.UpdatedFungibleAssets - ); + new Text("foo"), + outputAccount1.GetState(pk.ToAddress())); + Assert.Equal( + new Text("foo,bar"), + outputAccount1.GetState(addresses[0])); + Assert.Equal( + DumbAction.DumbCurrency * -30, + outputAccount1.GetBalance(pk.ToAddress(), DumbAction.DumbCurrency)); + Assert.Equal( + DumbAction.DumbCurrency * 10, + outputAccount1.GetBalance(addresses[1], DumbAction.DumbCurrency)); + Assert.Equal( + DumbAction.DumbCurrency * 20, + outputAccount1.GetBalance(addresses[2], DumbAction.DumbCurrency)); + var txExecution2 = getTxExecution(block3.Hash, tx2Error.Id); _logger.Verbose(nameof(txExecution2) + " = {@TxExecution}", txExecution2); - Assert.IsType(txExecution2); - var txFailure = (TxFailure)txExecution2; - Assert.Equal(block3.Hash, txFailure.BlockHash); - Assert.Equal(tx2Error.Id, txFailure.TxId); - Assert.Equal( + Assert.True(txExecution2.Fail); + Assert.Equal(block3.Hash, txExecution2.BlockHash); + Assert.Equal(tx2Error.Id, txExecution2.TxId); + Assert.Contains( $"{nameof(System)}.{nameof(ArgumentOutOfRangeException)}", - txFailure.ExceptionName - ); + txExecution2.ExceptionNames); + var txExecution3 = getTxExecution(block3.Hash, tx3Transfer.Id); _logger.Verbose(nameof(txExecution3) + " = {@TxExecution}", txExecution3); - Assert.IsType(txExecution3); - var txSuccess3 = (TxSuccess)txExecution3; + Assert.False(txExecution3.Fail); + var outputAccount3 = _blockChain.GetAccountState( + Assert.IsType>(txExecution3.OutputState)); Assert.Equal( - ImmutableDictionary>.Empty - .Add( - pk.ToAddress(), - ImmutableDictionary.Empty - .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * -35) - ) - .Add( - addresses[1], - ImmutableDictionary.Empty - .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 15) - ), - txSuccess3.UpdatedFungibleAssets - ); + DumbAction.DumbCurrency * -35, + outputAccount3.GetBalance(pk.ToAddress(), DumbAction.DumbCurrency)); + Assert.Equal( + DumbAction.DumbCurrency * 15, + outputAccount3.GetBalance(addresses[1], DumbAction.DumbCurrency)); } [SkippableFact] diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs index edc36f6275a..0264b51de79 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs @@ -2,18 +2,18 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Tests.Common; +using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Xunit; using static Libplanet.Action.State.KeyConverters; using static Libplanet.Tests.TestUtils; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; namespace Libplanet.Tests.Blockchain { @@ -144,23 +144,14 @@ public void ExecuteActions() [InlineData(false)] public void UpdateTxExecutions(bool getTxExecutionViaStore) { - void AssertTxSuccessesEqual(TxSuccess expected, TxExecution actual) + void AssertTxExecutionEqual(TxExecution expected, TxExecution actual) { - Assert.IsType(actual); - var success = (TxSuccess)actual; - Assert.Equal(expected.TxId, success.TxId); - Assert.Equal(expected.BlockHash, success.BlockHash); - Assert.Equal(expected.UpdatedStates, success.UpdatedStates); - Assert.Equal(expected.UpdatedFungibleAssets, success.UpdatedFungibleAssets); - } - - void AssertTxFailuresEqual(TxFailure expected, TxExecution actual) - { - Assert.IsType(actual); - var failure = (TxFailure)actual; - Assert.Equal(expected.TxId, failure.TxId); - Assert.Equal(expected.BlockHash, failure.BlockHash); - Assert.Equal(expected.ExceptionName, failure.ExceptionName); + Assert.Equal(expected.Fail, actual.Fail); + Assert.Equal(expected.TxId, actual.TxId); + Assert.Equal(expected.BlockHash, actual.BlockHash); + Assert.Equal(expected.InputState, actual.InputState); + Assert.Equal(expected.OutputState, actual.OutputState); + Assert.Equal(expected.ExceptionNames, actual.ExceptionNames); } Func getTxExecution @@ -174,37 +165,32 @@ Func getTxExecution Assert.Null(getTxExecution(_fx.Hash2, _fx.TxId2)); var random = new System.Random(); - var inputA = new TxSuccess( + var inputA = new TxExecution( _fx.Hash1, _fx.TxId1, - ImmutableDictionary.Empty.Add( - random.NextAddress(), - (Text)"state value" - ), - ImmutableDictionary>.Empty - .Add( - random.NextAddress(), - ImmutableDictionary.Empty.Add( - DumbAction.DumbCurrency, - DumbAction.DumbCurrency * 10 - ) - ) - ); - var inputB = new TxFailure( + false, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { string.Empty }); + var inputB = new TxExecution( _fx.Hash1, _fx.TxId2, - "AnExceptionName" - ); - var inputC = new TxFailure( + true, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { "AnExceptionName" }); + var inputC = new TxExecution( _fx.Hash2, _fx.TxId1, - "AnotherExceptionName" - ); + true, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { "AnotherExceptionName", "YetAnotherExceptionName" }); _blockChain.UpdateTxExecutions(new TxExecution[] { inputA, inputB, inputC }); - AssertTxSuccessesEqual(inputA, getTxExecution(_fx.Hash1, _fx.TxId1)); - AssertTxFailuresEqual(inputB, getTxExecution(_fx.Hash1, _fx.TxId2)); - AssertTxFailuresEqual(inputC, getTxExecution(_fx.Hash2, _fx.TxId1)); + AssertTxExecutionEqual(inputA, getTxExecution(_fx.Hash1, _fx.TxId1)); + AssertTxExecutionEqual(inputB, getTxExecution(_fx.Hash1, _fx.TxId2)); + AssertTxExecutionEqual(inputC, getTxExecution(_fx.Hash2, _fx.TxId1)); Assert.Null(getTxExecution(_fx.Hash2, _fx.TxId2)); } } diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs index 2d7ac97d3be..4ebffe3706b 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs @@ -314,7 +314,7 @@ public void ProposeBlockWithPendingTxs() tx.Id, txx ); - Assert.IsType(txx); + Assert.False(txx.Fail); Assert.Equal(block.Hash, txx.BlockHash); Assert.Equal(tx.Id, txx.TxId); Assert.Null(_blockChain.GetTxExecution(_blockChain.Genesis.Hash, tx.Id)); diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index 8c464c83c74..f343ff3ab57 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -11,9 +11,9 @@ using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; +using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; using Libplanet.Types.Tx; @@ -21,7 +21,6 @@ using Xunit; using Xunit.Abstractions; using static Libplanet.Tests.TestUtils; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; namespace Libplanet.Tests.Store { @@ -393,23 +392,14 @@ public void StoreBlock() [SkippableFact] public void TxExecution() { - void AssertTxSuccessesEqual(TxSuccess expected, TxExecution actual) + void AssertTxExecutionEqual(TxExecution expected, TxExecution actual) { - Assert.IsType(actual); - var success = (TxSuccess)actual; - Assert.Equal(expected.TxId, success.TxId); - Assert.Equal(expected.BlockHash, success.BlockHash); - Assert.Equal(expected.UpdatedStates, success.UpdatedStates); - Assert.Equal(expected.UpdatedFungibleAssets, success.UpdatedFungibleAssets); - } - - void AssertTxFailuresEqual(TxFailure expected, TxExecution actual) - { - Assert.IsType(actual); - var failure = (TxFailure)actual; - Assert.Equal(expected.TxId, failure.TxId); - Assert.Equal(expected.BlockHash, failure.BlockHash); - Assert.Equal(expected.ExceptionName, failure.ExceptionName); + Assert.Equal(expected.Fail, actual.Fail); + Assert.Equal(expected.TxId, actual.TxId); + Assert.Equal(expected.BlockHash, actual.BlockHash); + Assert.Equal(expected.InputState, actual.InputState); + Assert.Equal(expected.OutputState, actual.OutputState); + Assert.Equal(expected.ExceptionNames, actual.ExceptionNames); } Assert.Null(Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); @@ -417,52 +407,46 @@ void AssertTxFailuresEqual(TxFailure expected, TxExecution actual) Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId1)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId2)); - var random = new System.Random(); - var inputA = new TxSuccess( + var inputA = new TxExecution( Fx.Hash1, Fx.TxId1, - ImmutableDictionary.Empty.Add( - random.NextAddress(), - (Text)"state value" - ), - ImmutableDictionary>.Empty - .Add( - random.NextAddress(), - ImmutableDictionary.Empty.Add( - DumbAction.DumbCurrency, - DumbAction.DumbCurrency * 10 - ) - ) - ); + false, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { string.Empty }); Fx.Store.PutTxExecution(inputA); - AssertTxSuccessesEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); + AssertTxExecutionEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId2)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId1)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId2)); - var inputB = new TxFailure( + var inputB = new TxExecution( Fx.Hash1, Fx.TxId2, - "AnExceptionName" - ); + true, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { "AnExceptionName" }); Fx.Store.PutTxExecution(inputB); - AssertTxSuccessesEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); - AssertTxFailuresEqual(inputB, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId2)); + AssertTxExecutionEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); + AssertTxExecutionEqual(inputB, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId2)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId1)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId2)); - var inputC = new TxFailure( + var inputC = new TxExecution( Fx.Hash2, Fx.TxId1, - "AnotherExceptionName" - ); + true, + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new HashDigest(TestUtils.GetRandomBytes(HashDigest.Size)), + new List() { "AnotherExceptionName", "YetAnotherExceptionName" }); Fx.Store.PutTxExecution(inputC); - AssertTxSuccessesEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); - AssertTxFailuresEqual(inputB, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId2)); - AssertTxFailuresEqual(inputC, Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId1)); + AssertTxExecutionEqual(inputA, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId1)); + AssertTxExecutionEqual(inputB, Fx.Store.GetTxExecution(Fx.Hash1, Fx.TxId2)); + AssertTxExecutionEqual(inputC, Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId1)); Assert.Null(Fx.Store.GetTxExecution(Fx.Hash2, Fx.TxId2)); } diff --git a/Libplanet.Tests/Tx/TxExecutionTest.cs b/Libplanet.Tests/Tx/TxExecutionTest.cs new file mode 100644 index 00000000000..aa1c40841b8 --- /dev/null +++ b/Libplanet.Tests/Tx/TxExecutionTest.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Types.Assets; +using Libplanet.Types.Tx; +using Xunit; + +namespace Libplanet.Tests.Tx +{ + public class TxExecutionTest + { + [Theory] + [InlineData(false, "")] + [InlineData(true, "SomeException")] + public void Constructor(bool fail, string exceptionName) + { + var random = new Random(); + var blockHash = random.NextBlockHash(); + var txId = random.NextTxId(); + var inputState = random.NextHashDigest(); + var outputState = random.NextHashDigest(); + var exceptionNames = new List() { exceptionName }; + var execution = new TxExecution( + blockHash, + txId, + fail, + inputState, + outputState, + exceptionNames); + Assert.Equal(blockHash, execution.BlockHash); + Assert.Equal(txId, execution.TxId); + Assert.Equal(fail, execution.Fail); + Assert.Equal(inputState, execution.InputState); + Assert.Equal(outputState, execution.OutputState); + Assert.Equal(exceptionNames, execution.ExceptionNames); + } + + [Fact] + public void EncodeDecode() + { + var random = new Random(); + var execution = new TxExecution( + random.NextBlockHash(), + random.NextTxId(), + true, + random.NextHashDigest(), + random.NextHashDigest(), + new List() { string.Empty, "SomeException", "AnotherException" }); + var encoded = execution.ToBencodex(); + var decoded = new TxExecution( + execution.BlockHash, + execution.TxId, + encoded); + Assert.Equal(execution.BlockHash, decoded.BlockHash); + Assert.Equal(execution.TxId, decoded.TxId); + Assert.Equal(execution.Fail, decoded.Fail); + Assert.Equal(execution.InputState, decoded.InputState); + Assert.Equal(execution.OutputState, decoded.OutputState); + Assert.Equal(execution.ExceptionNames, decoded.ExceptionNames); + } + + [Fact] + public void ConstructorWithExceptions() + { + var random = new Random(); + var blockHash = random.NextBlockHash(); + var txId = random.NextTxId(); + var inputState = random.NextHashDigest(); + var outputState = random.NextHashDigest(); + var exceptions = new List() { null, new ArgumentException("Message") }; + var execution = new TxExecution( + blockHash, + txId, + true, + inputState, + outputState, + exceptions); + Assert.Equal(blockHash, execution.BlockHash); + Assert.Equal(txId, execution.TxId); + Assert.True(execution.Fail); + Assert.Equal(inputState, execution.InputState); + Assert.Equal(outputState, execution.OutputState); + Assert.Equal( + exceptions + .Select(exception => exception is Exception e + ? e.GetType().FullName + : string.Empty), + execution.ExceptionNames); + } + + [Fact] + public void DecodeFailLegacy() + { + var random = new Random(); + var blockHash = random.NextBlockHash(); + var txId = random.NextTxId(); + + Dictionary legacyEncoded = Dictionary.Empty + .Add("fail", true) + .Add("exc", "SomeException"); + var failExecution = new TxExecution( + blockHash, + txId, + legacyEncoded); + Assert.Equal(blockHash, failExecution.BlockHash); + Assert.Equal(txId, failExecution.TxId); + Assert.True(failExecution.Fail); + Assert.Null(failExecution.InputState); + Assert.Null(failExecution.OutputState); + Assert.Null(failExecution.ExceptionNames); + } + + [Fact] + public void DecodeSuccessLegacy() + { + var random = new Random(); + var blockHash = random.NextBlockHash(); + var txId = random.NextTxId(); + + // Note: Actual format for sDelta and updatedFAVs doesn't really matter, + // it is important decoding doesn't throw an exception. + var currency = Currency.Uncapped("FOO", 0, null); + Dictionary legacyEncoded = Dictionary.Empty + .Add("fail", false) + .Add("sDelta", Dictionary.Empty + .Add(random.NextAddress().ByteArray, random.NextAddress().ByteArray)) + .Add("updatedFAVs", List.Empty + .Add(currency.Serialize()) + .Add(123)); + var successExecution = new TxExecution( + blockHash, + txId, + legacyEncoded); + Assert.Equal(blockHash, successExecution.BlockHash); + Assert.Equal(txId, successExecution.TxId); + Assert.False(successExecution.Fail); + Assert.Null(successExecution.InputState); + Assert.Null(successExecution.OutputState); + Assert.Null(successExecution.ExceptionNames); + } + } +} diff --git a/Libplanet.Tests/Tx/TxFailureTest.cs b/Libplanet.Tests/Tx/TxFailureTest.cs deleted file mode 100644 index 6897580aa97..00000000000 --- a/Libplanet.Tests/Tx/TxFailureTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Libplanet.Types.Blocks; -using Libplanet.Types.Tx; -using Xunit; - -namespace Libplanet.Tests.Tx -{ - public class TxFailureTest - { - private readonly BlockHash _blockHash; - private readonly TxId _txid; - private readonly TxFailure _fx; - - [SuppressMessage( - "Major Code Smell", - "S3928", - Justification = "The ArgumentNullException instance made here is a just example." - )] - public TxFailureTest() - { - var random = new Random(); - _blockHash = random.NextBlockHash(); - _txid = random.NextTxId(); - _fx = new TxFailure( - _blockHash, - _txid, - new ArgumentNullException("foo")); - } - - [Fact] - public void ConstructorWithExceptionObject() - { - Assert.Equal(_blockHash, _fx.BlockHash); - Assert.Equal(_txid, _fx.TxId); - Assert.Equal( - $"{nameof(System)}.{nameof(ArgumentNullException)}", - _fx.ExceptionName); - } - - [Fact] - public void Constructor() - { - var f = new TxFailure( - _blockHash, - _txid, - nameof(ArgumentNullException) - ); - Assert.Equal(_blockHash, f.BlockHash); - Assert.Equal(_txid, f.TxId); - Assert.Equal(nameof(ArgumentNullException), f.ExceptionName); - } - } -} diff --git a/Libplanet.Tests/Tx/TxSuccessTest.cs b/Libplanet.Tests/Tx/TxSuccessTest.cs deleted file mode 100644 index fecce72eece..00000000000 --- a/Libplanet.Tests/Tx/TxSuccessTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Libplanet.Types.Blocks; -using Libplanet.Types.Tx; -using Xunit; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; - -namespace Libplanet.Tests.Tx -{ - public class TxSuccessTest - { - private static readonly Currency[] Currencies = new[] { "FOO", "BAR", "BAZ", "QUX" } - .Select(ticker => Currency.Uncapped(ticker, 0, null)) - .ToArray(); - - private readonly BlockHash _blockHash; - private readonly TxId _txid; - private readonly ImmutableDictionary _updatedStates; - - private readonly ImmutableDictionary> - _updatedFungibleAssets; - - private readonly TxSuccess _fx; - - public TxSuccessTest() - { - var random = new Random(); - _blockHash = random.NextBlockHash(); - _txid = random.NextTxId(); - _updatedStates = Enumerable.Repeat(random, 5).ToImmutableDictionary( - RandomExtensions.NextAddress, - _ => (IValue)new Bencodex.Types.Integer(random.Next()) - ); - var currencies = Currencies.ToList(); - _updatedFungibleAssets = Enumerable.Repeat(random, 5).ToImmutableDictionary( - RandomExtensions.NextAddress, - _ => (IImmutableDictionary)Enumerable.Repeat( - 0, - random.Next(currencies.Count)) - .Select(__ => - { - int i = random.Next(currencies.Count); - Currency c = currencies[i]; - currencies.RemoveAt(i); - return c; - }) - .ToImmutableDictionary( - c => c, - c => c * random.Next() - ) - ); - _fx = new TxSuccess( - _blockHash, - _txid, - _updatedStates, - _updatedFungibleAssets - ); - } - - [Fact] - public void ConstructorBuilder() - { - Assert.Equal(_blockHash, _fx.BlockHash); - Assert.Equal(_txid, _fx.TxId); - Assert.Equal(_updatedStates, _fx.UpdatedStates); - Assert.Equal(_updatedFungibleAssets, _fx.UpdatedFungibleAssets); - } - } -} diff --git a/Libplanet.Types/Tx/TxExecution.cs b/Libplanet.Types/Tx/TxExecution.cs index 4fb13998c70..99bdeb3bcb7 100644 --- a/Libplanet.Types/Tx/TxExecution.cs +++ b/Libplanet.Types/Tx/TxExecution.cs @@ -86,16 +86,19 @@ private TxExecution( TxId = txId; if (!encoded.TryGetValue(FailKey, out IValue fail) || - !(fail is Bencodex.Types.Boolean failBoolean) || - (failBoolean.Value == false)) + !(fail is Bencodex.Types.Boolean failBoolean)) { throw new ArgumentException($"Invalid fail key value: {fail}"); } + else + { + Fail = failBoolean.Value; + } if (encoded.TryGetValue(InputStateKey, out IValue input) && input is Binary inputBinary) { - InputState = HashDigest.DeriveFrom(inputBinary.ByteArray); + InputState = new HashDigest(inputBinary.ByteArray); } else { @@ -103,9 +106,9 @@ private TxExecution( } if (encoded.TryGetValue(OutputStateKey, out IValue output) && - input is Binary outputBinary) + output is Binary outputBinary) { - InputState = HashDigest.DeriveFrom(outputBinary.ByteArray); + OutputState = new HashDigest(outputBinary.ByteArray); } else { diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index 34eda701b2b..c4d7f19bded 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -65,6 +65,9 @@ IReadOnlyList evaluations List exceptions = group.Item2 .Select(eval => eval.Exception) + .Select(exception => exception is { } e && e.InnerException is { } i + ? i + : exception) .ToList(); yield return new TxExecution( @@ -73,7 +76,7 @@ IReadOnlyList evaluations exceptions.Any(exception => exception is { }), trie.Hash, nextTrie.Hash, - exceptions); + exceptions.ToList()); count++; trie = nextTrie; From 978b7684b3d8aad7ba2e6cf8b42c6aec5173e9a6 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 10:00:59 +0900 Subject: [PATCH 22/31] Refactor TxResult to partially accommodate TxExecution changes --- .../GraphTypes/TxResultTypeTest.cs | 46 +++++-------------- .../Queries/TransactionQueryGeneratedTest.cs | 18 ++++---- Libplanet.Explorer/GraphTypes/TxResult.cs | 7 +-- Libplanet.Explorer/GraphTypes/TxResultType.cs | 6 +-- .../Queries/TransactionQuery.cs | 2 +- 5 files changed, 29 insertions(+), 50 deletions(-) diff --git a/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs b/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs index 0781e99d71f..7b849e5be4e 100644 --- a/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs +++ b/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs @@ -21,7 +21,7 @@ public async void Query(TxResult txResult, IDictionary expected) txStatus blockIndex blockHash - exceptionName + exceptionNames updatedStates { address state @@ -60,17 +60,17 @@ public static IEnumerable TestCases() { TxStatus.SUCCESS, 0, "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", + new List() { "" }, null, - ImmutableDictionary.Empty, - ImmutableDictionary>.Empty + null ), new Dictionary { ["txStatus"] = "SUCCESS", ["blockIndex"] = 0L, ["blockHash"] = "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", - ["exceptionName"] = null, - ["updatedStates"] = new object[0], - ["updatedFungibleAssets"] = new object[0], + ["exceptionNames"] = new string[] { "" }, + ["updatedStates"] = null, + ["updatedFungibleAssets"] = null, } }, new object[] { @@ -78,41 +78,17 @@ public static IEnumerable TestCases() { TxStatus.SUCCESS, 0, "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", + new List() { "" }, null, - ImmutableDictionary.Empty - .Add(address, Bencodex.Types.Null.Value), - ImmutableDictionary>.Empty - .Add( - address, - ImmutableDictionary.Empty - .Add(KRW, KRW * 20000) - ) + null ), new Dictionary { ["txStatus"] = "SUCCESS", ["blockIndex"] = 0L, ["blockHash"] = "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", - ["exceptionName"] = null, - ["updatedStates"] = new object[] { - new Dictionary { - ["address"] = address.ToString(), - ["state"] = new byte[] { 110, }, - }, - }, - ["updatedFungibleAssets"] = new object[] { - new Dictionary { - ["address"] = address.ToString(), - ["fungibleAssetValues"] = new object[] { - new Dictionary { - ["currency"] = new Dictionary { - ["ticker"] = KRW.Ticker, - ["decimalPlaces"] = KRW.DecimalPlaces, - }, - ["quantity"] = "20000", - }, - }, - }, - }, + ["exceptionNames"] = new string[] { "" }, + ["updatedStates"] = null, + ["updatedFungibleAssets"] = null, } } }; diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs index db2021cce63..bbfa248715f 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs @@ -64,24 +64,24 @@ public async Task TransactionResult() Assert.Equal("SUCCESS", queryResult.TxStatus); Assert.Equal(successBlock.Index, queryResult.BlockIndex); Assert.Equal(successBlock.Hash.ToString(), queryResult.BlockHash); - Assert.Null(queryResult.ExceptionName); + Assert.Equal(new string[] { "" }, queryResult.ExceptionNames); queryResult = await ExecuteTransactionResultQueryAsync(failTx.Id); Assert.Equal("FAILURE", queryResult.TxStatus); Assert.Equal(failBlock.Index, queryResult.BlockIndex); Assert.Equal(failBlock.Hash.ToString(), queryResult.BlockHash); Assert.Equal( - "Libplanet.Action.State.CurrencyPermissionException", - queryResult.ExceptionName); + new string[] { "Libplanet.Action.State.CurrencyPermissionException" }, + queryResult.ExceptionNames); queryResult = await ExecuteTransactionResultQueryAsync(new TxId()); Assert.Equal("INVALID", queryResult.TxStatus); Assert.Null(queryResult.BlockIndex); Assert.Null(queryResult.BlockHash); - Assert.Null(queryResult.ExceptionName); + Assert.Null(queryResult.ExceptionNames); queryResult = await ExecuteTransactionResultQueryAsync(stagingTx.Id); Assert.Equal("STAGING", queryResult.TxStatus); Assert.Null(queryResult.BlockIndex); Assert.Null(queryResult.BlockHash); - Assert.Null(queryResult.ExceptionName); + Assert.Null(queryResult.ExceptionNames); } [Fact] @@ -300,7 +300,7 @@ await Source.Index.GetContainedBlockHashByTxIdAsync(expected[i].Id) } private async Task< - (string TxStatus, long? BlockIndex, string? BlockHash, string? ExceptionName)> + (string TxStatus, long? BlockIndex, string? BlockHash, string[]? ExceptionNames)> ExecuteTransactionResultQueryAsync(TxId txId) { ExecutionResult result = await ExecuteQueryAsync(@$" @@ -310,7 +310,7 @@ private async Task< txStatus blockIndex blockHash - exceptionName + exceptionNames }} }} ", QueryGraph, source: Source); @@ -319,10 +319,12 @@ private async Task< IDictionary resultDict = (IDictionary)Assert.IsAssignableFrom>( resultData.ToValue())["transactionResult"]; + var exceptionNames = ((object[]?)resultDict["exceptionNames"])?.Select( + name => Assert.IsType(name)).ToArray(); return ( (string)resultDict["txStatus"], (long?)resultDict["blockIndex"], (string?)resultDict["blockHash"], - (string?)resultDict["exceptionName"]); + (string[]?)exceptionNames); } } diff --git a/Libplanet.Explorer/GraphTypes/TxResult.cs b/Libplanet.Explorer/GraphTypes/TxResult.cs index eb9a518762a..f45d842ae07 100644 --- a/Libplanet.Explorer/GraphTypes/TxResult.cs +++ b/Libplanet.Explorer/GraphTypes/TxResult.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using Bencodex.Types; using Libplanet.Crypto; @@ -12,7 +13,7 @@ public TxResult( TxStatus status, long? blockIndex, string? blockHash, - string? exceptionName, + List? exceptionNames, IImmutableDictionary? updatedStates, IImmutableDictionary>? updatedFungibleAssets @@ -21,7 +22,7 @@ public TxResult( TxStatus = status; BlockIndex = blockIndex; BlockHash = blockHash; - ExceptionName = exceptionName; + ExceptionNames = exceptionNames; UpdatedStates = updatedStates; UpdatedFungibleAssets = updatedFungibleAssets; } @@ -32,7 +33,7 @@ public TxResult( public string? BlockHash { get; private set; } - public string? ExceptionName { get; private set; } + public List? ExceptionNames { get; private set; } public IImmutableDictionary? UpdatedStates { get; } diff --git a/Libplanet.Explorer/GraphTypes/TxResultType.cs b/Libplanet.Explorer/GraphTypes/TxResultType.cs index 70a1a16022d..953efa54e05 100644 --- a/Libplanet.Explorer/GraphTypes/TxResultType.cs +++ b/Libplanet.Explorer/GraphTypes/TxResultType.cs @@ -28,10 +28,10 @@ public TxResultType() resolve: context => context.Source.BlockHash ); - Field( - nameof(TxResult.ExceptionName), + Field>( + nameof(TxResult.ExceptionNames), description: "The name of exception. (when only failed)", - resolve: context => context.Source.ExceptionName + resolve: context => context.Source.ExceptionNames ); Field>>( diff --git a/Libplanet.Explorer/Queries/TransactionQuery.cs b/Libplanet.Explorer/Queries/TransactionQuery.cs index fa7f2f4b990..da64a3b359c 100644 --- a/Libplanet.Explorer/Queries/TransactionQuery.cs +++ b/Libplanet.Explorer/Queries/TransactionQuery.cs @@ -272,7 +272,7 @@ index is null execution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS, txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), - null, + execution.ExceptionNames, null, null); } From 5c3dcdbca56d8ea0849555229c722e40f7191971 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 10:30:51 +0900 Subject: [PATCH 23/31] Clean up TxResult API --- .../GraphTypes/TxResultTypeTest.cs | 28 +---------- Libplanet.Explorer/GraphTypes/TxResult.cs | 18 +------ Libplanet.Explorer/GraphTypes/TxResultType.cs | 48 ------------------- .../Queries/TransactionQuery.cs | 13 +---- 4 files changed, 5 insertions(+), 102 deletions(-) diff --git a/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs b/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs index 7b849e5be4e..7fa38783b7e 100644 --- a/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs +++ b/Libplanet.Explorer.Tests/GraphTypes/TxResultTypeTest.cs @@ -22,20 +22,6 @@ public async void Query(TxResult txResult, IDictionary expected) blockIndex blockHash exceptionNames - updatedStates { - address - state - } - updatedFungibleAssets { - address - fungibleAssetValues { - currency { - ticker - decimalPlaces - } - quantity - } - } }"; var txResultType = new TxResultType(); @@ -52,25 +38,19 @@ public async void Query(TxResult txResult, IDictionary expected) } public static IEnumerable TestCases() { - Currency KRW = Currency.Uncapped("KRW", 18, null); - Address address = new Address("76ca86fa821c8241f9422c22b1386021047faf0d"); return new object[][] { new object[] { new TxResult( TxStatus.SUCCESS, 0, "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", - new List() { "" }, - null, - null + new List() { "" } ), new Dictionary { ["txStatus"] = "SUCCESS", ["blockIndex"] = 0L, ["blockHash"] = "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", ["exceptionNames"] = new string[] { "" }, - ["updatedStates"] = null, - ["updatedFungibleAssets"] = null, } }, new object[] { @@ -78,17 +58,13 @@ public static IEnumerable TestCases() { TxStatus.SUCCESS, 0, "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", - new List() { "" }, - null, - null + new List() { "" } ), new Dictionary { ["txStatus"] = "SUCCESS", ["blockIndex"] = 0L, ["blockHash"] = "45bcaa4c0b00f4f31eb61577e595ea58fb69c7df3ee612aa6eea945bbb0ce39d", ["exceptionNames"] = new string[] { "" }, - ["updatedStates"] = null, - ["updatedFungibleAssets"] = null, } } }; diff --git a/Libplanet.Explorer/GraphTypes/TxResult.cs b/Libplanet.Explorer/GraphTypes/TxResult.cs index f45d842ae07..754df551866 100644 --- a/Libplanet.Explorer/GraphTypes/TxResult.cs +++ b/Libplanet.Explorer/GraphTypes/TxResult.cs @@ -1,9 +1,4 @@ using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using FAV = Libplanet.Types.Assets.FungibleAssetValue; namespace Libplanet.Explorer.GraphTypes { @@ -13,18 +8,12 @@ public TxResult( TxStatus status, long? blockIndex, string? blockHash, - List? exceptionNames, - IImmutableDictionary? updatedStates, - IImmutableDictionary>? - updatedFungibleAssets - ) + List? exceptionNames) { TxStatus = status; BlockIndex = blockIndex; BlockHash = blockHash; ExceptionNames = exceptionNames; - UpdatedStates = updatedStates; - UpdatedFungibleAssets = updatedFungibleAssets; } public TxStatus TxStatus { get; private set; } @@ -34,10 +23,5 @@ public TxResult( public string? BlockHash { get; private set; } public List? ExceptionNames { get; private set; } - - public IImmutableDictionary? UpdatedStates { get; } - - public IImmutableDictionary>? - UpdatedFungibleAssets { get; } } } diff --git a/Libplanet.Explorer/GraphTypes/TxResultType.cs b/Libplanet.Explorer/GraphTypes/TxResultType.cs index 953efa54e05..0cd5ab45a89 100644 --- a/Libplanet.Explorer/GraphTypes/TxResultType.cs +++ b/Libplanet.Explorer/GraphTypes/TxResultType.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using GraphQL.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -33,53 +32,6 @@ public TxResultType() description: "The name of exception. (when only failed)", resolve: context => context.Source.ExceptionNames ); - - Field>>( - nameof(TxResult.UpdatedStates), - resolve: context => context.Source.UpdatedStates? - .Select(pair => new UpdatedState(pair.Key, pair.Value)) - ); - - Field>>( - nameof(TxResult.UpdatedFungibleAssets), - resolve: context => context.Source.UpdatedFungibleAssets? - .Select(pair => new FungibleAssetBalances(pair.Key, pair.Value.Values)) - ); - } - - public record UpdatedState(Address Address, Bencodex.Types.IValue? State); - - public class UpdatedStateType : ObjectGraphType - { - public UpdatedStateType() - { - Field>( - nameof(UpdatedState.Address), - resolve: context => context.Source.Address - ); - Field( - nameof(UpdatedState.State), - resolve: context => context.Source.State - ); - } - } - - public record FungibleAssetBalances( - Address Address, IEnumerable FungibleAssetValues); - - public class FungibleAssetBalancesType : ObjectGraphType - { - public FungibleAssetBalancesType() - { - Field>( - nameof(FungibleAssetBalances.Address), - resolve: context => context.Source.Address - ); - Field>>>( - nameof(FungibleAssetBalances.FungibleAssetValues), - resolve: context => context.Source.FungibleAssetValues - ); - } } } } diff --git a/Libplanet.Explorer/Queries/TransactionQuery.cs b/Libplanet.Explorer/Queries/TransactionQuery.cs index da64a3b359c..667528f0242 100644 --- a/Libplanet.Explorer/Queries/TransactionQuery.cs +++ b/Libplanet.Explorer/Queries/TransactionQuery.cs @@ -248,15 +248,11 @@ index is null TxStatus.STAGING, null, null, - null, - null, null) : new TxResult( TxStatus.INVALID, null, null, - null, - null, null); } @@ -272,9 +268,7 @@ index is null execution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS, txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), - execution.ExceptionNames, - null, - null); + execution.ExceptionNames); } catch (Exception) { @@ -282,10 +276,7 @@ index is null TxStatus.INVALID, null, null, - null, - null, - null - ); + null); } } ); From 00f4bead6e7e3b720cef4f918c604b3bc18bb9dc Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 11:27:02 +0900 Subject: [PATCH 24/31] Changed individual elements of ExceptionNames to be nullable --- .../Queries/TransactionQueryGeneratedTest.cs | 8 +-- Libplanet.Tests/Tx/TxExecutionTest.cs | 4 +- Libplanet.Types/Tx/TxExecution.cs | 49 +++++++++++++------ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs index bbfa248715f..fda7e5b6f05 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs @@ -64,7 +64,7 @@ public async Task TransactionResult() Assert.Equal("SUCCESS", queryResult.TxStatus); Assert.Equal(successBlock.Index, queryResult.BlockIndex); Assert.Equal(successBlock.Hash.ToString(), queryResult.BlockHash); - Assert.Equal(new string[] { "" }, queryResult.ExceptionNames); + Assert.Equal(new string?[] { null }, queryResult.ExceptionNames); queryResult = await ExecuteTransactionResultQueryAsync(failTx.Id); Assert.Equal("FAILURE", queryResult.TxStatus); Assert.Equal(failBlock.Index, queryResult.BlockIndex); @@ -300,7 +300,7 @@ await Source.Index.GetContainedBlockHashByTxIdAsync(expected[i].Id) } private async Task< - (string TxStatus, long? BlockIndex, string? BlockHash, string[]? ExceptionNames)> + (string TxStatus, long? BlockIndex, string? BlockHash, string?[]? ExceptionNames)> ExecuteTransactionResultQueryAsync(TxId txId) { ExecutionResult result = await ExecuteQueryAsync(@$" @@ -320,11 +320,11 @@ private async Task< (IDictionary)Assert.IsAssignableFrom>( resultData.ToValue())["transactionResult"]; var exceptionNames = ((object[]?)resultDict["exceptionNames"])?.Select( - name => Assert.IsType(name)).ToArray(); + name => name is { } n ? Assert.IsType(n) : null).ToArray(); return ( (string)resultDict["txStatus"], (long?)resultDict["blockIndex"], (string?)resultDict["blockHash"], - (string[]?)exceptionNames); + (string?[]?)exceptionNames); } } diff --git a/Libplanet.Tests/Tx/TxExecutionTest.cs b/Libplanet.Tests/Tx/TxExecutionTest.cs index aa1c40841b8..d85f45b19e5 100644 --- a/Libplanet.Tests/Tx/TxExecutionTest.cs +++ b/Libplanet.Tests/Tx/TxExecutionTest.cs @@ -47,7 +47,7 @@ public void EncodeDecode() true, random.NextHashDigest(), random.NextHashDigest(), - new List() { string.Empty, "SomeException", "AnotherException" }); + new List() { null, "SomeException", "AnotherException" }); var encoded = execution.ToBencodex(); var decoded = new TxExecution( execution.BlockHash, @@ -86,7 +86,7 @@ public void ConstructorWithExceptions() exceptions .Select(exception => exception is Exception e ? e.GetType().FullName - : string.Empty), + : null), execution.ExceptionNames); } diff --git a/Libplanet.Types/Tx/TxExecution.cs b/Libplanet.Types/Tx/TxExecution.cs index 99bdeb3bcb7..abcc20d5c78 100644 --- a/Libplanet.Types/Tx/TxExecution.cs +++ b/Libplanet.Types/Tx/TxExecution.cs @@ -41,7 +41,7 @@ public TxExecution( exceptions .Select(exception => exception is { } e ? e.GetType().FullName - : string.Empty) + : null) .ToList()) { } @@ -52,7 +52,7 @@ public TxExecution( bool fail, HashDigest inputState, HashDigest outputState, - List exceptionNames) + List exceptionNames) { BlockHash = blockHash; TxId = txId; @@ -66,13 +66,16 @@ public TxExecution( public TxExecution( BlockHash blockHash, TxId txId, - IValue encoded) + IValue bencoded) : this( blockHash, txId, - encoded is Dictionary dict + bencoded is Dictionary dict ? dict - : throw new ArgumentException()) + : throw new ArgumentException( + $"Given {nameof(bencoded)} must be of type " + + $"{typeof(Bencodex.Types.Dictionary)}: {bencoded.GetType()}", + nameof(bencoded))) #pragma warning restore SA1118 { } @@ -80,22 +83,29 @@ encoded is Dictionary dict private TxExecution( BlockHash blockHash, TxId txId, - Dictionary encoded) + Dictionary bencoded) { BlockHash = blockHash; TxId = txId; - if (!encoded.TryGetValue(FailKey, out IValue fail) || - !(fail is Bencodex.Types.Boolean failBoolean)) + if (!bencoded.TryGetValue(FailKey, out IValue fail)) { - throw new ArgumentException($"Invalid fail key value: {fail}"); + throw new ArgumentException( + $"Given {nameof(bencoded)} is missing fail value", + nameof(bencoded)); + } + else if (!(fail is Bencodex.Types.Boolean failBoolean)) + { + throw new ArgumentException( + $"Given {nameof(bencoded)} has an invalid fail value: {fail}", + nameof(bencoded)); } else { Fail = failBoolean.Value; } - if (encoded.TryGetValue(InputStateKey, out IValue input) && + if (bencoded.TryGetValue(InputStateKey, out IValue input) && input is Binary inputBinary) { InputState = new HashDigest(inputBinary.ByteArray); @@ -105,7 +115,7 @@ private TxExecution( InputState = null; } - if (encoded.TryGetValue(OutputStateKey, out IValue output) && + if (bencoded.TryGetValue(OutputStateKey, out IValue output) && output is Binary outputBinary) { OutputState = new HashDigest(outputBinary.ByteArray); @@ -115,13 +125,17 @@ private TxExecution( OutputState = null; } - if (encoded.TryGetValue(ExceptionNamesKey, out IValue exceptions) && + if (bencoded.TryGetValue(ExceptionNamesKey, out IValue exceptions) && exceptions is List exceptionsList) { ExceptionNames = exceptionsList .Select(value => value is Text t - ? t.Value - : throw new ArgumentException()) + ? (string?)t.Value + : value is Null + ? (string?)null + : throw new ArgumentException( + $"Expected either {nameof(Text)} or {nameof(Null)} " + + $"but got {value.GetType()}")) .ToList(); } else @@ -149,7 +163,7 @@ private TxExecution( public HashDigest? OutputState { get; } - public List? ExceptionNames { get; } + public List? ExceptionNames { get; } public IValue ToBencodex() { @@ -170,7 +184,10 @@ public IValue ToBencodex() { dict = dict.Add( ExceptionNamesKey, - new List(exceptionNames.Select(exceptionName => new Text(exceptionName)))); + new List(exceptionNames + .Select(exceptionName => exceptionName is { } name + ? (IValue)new Text(exceptionName) + : (IValue)Null.Value))); } return dict; From 45812cabb49a1b8e0a1b7f1901400dada729df4e Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 11:33:06 +0900 Subject: [PATCH 25/31] Docs --- Libplanet.Explorer/GraphTypes/TxResult.cs | 4 +-- Libplanet.Explorer/GraphTypes/TxResultType.cs | 3 --- Libplanet.Types/Tx/TxExecution.cs | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Libplanet.Explorer/GraphTypes/TxResult.cs b/Libplanet.Explorer/GraphTypes/TxResult.cs index 754df551866..00f3c3a3b24 100644 --- a/Libplanet.Explorer/GraphTypes/TxResult.cs +++ b/Libplanet.Explorer/GraphTypes/TxResult.cs @@ -8,7 +8,7 @@ public TxResult( TxStatus status, long? blockIndex, string? blockHash, - List? exceptionNames) + List? exceptionNames) { TxStatus = status; BlockIndex = blockIndex; @@ -22,6 +22,6 @@ public TxResult( public string? BlockHash { get; private set; } - public List? ExceptionNames { get; private set; } + public List? ExceptionNames { get; private set; } } } diff --git a/Libplanet.Explorer/GraphTypes/TxResultType.cs b/Libplanet.Explorer/GraphTypes/TxResultType.cs index 0cd5ab45a89..539865e7676 100644 --- a/Libplanet.Explorer/GraphTypes/TxResultType.cs +++ b/Libplanet.Explorer/GraphTypes/TxResultType.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using GraphQL.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; namespace Libplanet.Explorer.GraphTypes { diff --git a/Libplanet.Types/Tx/TxExecution.cs b/Libplanet.Types/Tx/TxExecution.cs index abcc20d5c78..5d98cc0b35b 100644 --- a/Libplanet.Types/Tx/TxExecution.cs +++ b/Libplanet.Types/Tx/TxExecution.cs @@ -157,12 +157,37 @@ private TxExecution( [Pure] public TxId TxId { get; } + /// + /// Whether every action in the was + /// executed without throwing and . + /// public bool Fail { get; } + /// + /// The state before the execution of the . + /// + /// + /// This is marked -able for backward compatibility. + /// public HashDigest? InputState { get; } + /// + /// The state after the execution of the . + /// + /// + /// This is marked -able for backward compatibility. + /// public HashDigest? OutputState { get; } + /// + /// The list of names thrown by actions + /// in the . A value of + /// as an element represents no being thrown for + /// the action of the same index. + /// + /// + /// This is marked -able for backward compatibility. + /// public List? ExceptionNames { get; } public IValue ToBencodex() From 9453c71ae5549a178e176912b25ed15e62ee32f4 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 14:04:14 +0900 Subject: [PATCH 26/31] Changelog --- CHANGES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 710a446002a..aca7e306747 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,17 @@ To be released. - Removed `TxFailure.ExceptionMetadata` property. [[#3428]] - Removed `ISerializable` interface from `TxExecution`, `TxSuccess`, and `TxFailure`. [[#3428]] + - Removed `TxSuccess` and `TxFailure` class. [[#3429]] + - Changed `TxExecution` class as `sealed` from `abstract.` [[#3429]] + - All properties of `TxExecution` except `BlockHash` and `TxId` were + overhauled. [[#3429]] + - (Libplanet.Store) Removed `IStore.PutTxExecution(TxSuccess)` and + `IStore.PutTxExecution(TxFailure)`; + added `IStore.PutTxExecution(TxExecution)`. [[#3429]] + - (Libplanet.Explorer) Removed `TxResult.ExceptionName` of type `string?` + and added `TxResult.ExceptionNames` of type `List?`. [[#3429]] + - (Libplanet.Explorer) Removed `TxResult.UpdatedStates` and + `TxResult.UpdatedFungibleAssets`. [[#3429]] ### Backward-incompatible network protocol changes @@ -43,6 +54,7 @@ To be released. [#3425]: https://github.com/planetarium/libplanet/pull/3425 [#3427]: https://github.com/planetarium/libplanet/pull/3427 [#3428]: https://github.com/planetarium/libplanet/pull/3428 +[#3429]: https://github.com/planetarium/libplanet/pull/3429 Version 3.3.1 From 78a827619806b8941cbc96d3f51f5e4d9257911f Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 21 Sep 2023 17:42:14 +0900 Subject: [PATCH 27/31] Remove clutter --- Libplanet/Blockchain/BlockChain.Swap.cs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 33f87ede385..5ee46a00c25 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -124,7 +124,6 @@ HashSet GetTxIdsWithRange( oldTip: oldTip, newTip: newTip, branchpoint: branchpoint, - rewindPath: rewindPath, fastForwardPath: fastForwardPath); } finally @@ -140,7 +139,6 @@ internal void RenderSwap( Block oldTip, Block newTip, Block branchpoint, - IReadOnlyList rewindPath, IReadOnlyList fastForwardPath) { if (render) @@ -172,23 +170,21 @@ internal void RenderFastForward( { _logger.Debug("Rendering actions in new chain"); - long count = 0; foreach (BlockHash hash in fastForwardPath) { Block block = Store.GetBlock(hash); ImmutableList evaluations = ActionEvaluator.Evaluate(block).ToImmutableList(); - count += RenderActions( + RenderActions( evaluations: evaluations, - block: block - ); + block: block); } _logger.Debug( - "{MethodName}() completed rendering {Count} actions", + "{MethodName}() completed rendering actions in {Count} blocks", nameof(Swap), - count); + fastForwardPath.Count); foreach (IActionRenderer renderer in ActionRenderers) { @@ -203,8 +199,7 @@ internal void RenderFastForward( /// s of the block. If it is /// , evaluate actions of the again. /// to render actions. - /// The number of actions rendered. - internal long RenderActions( + internal void RenderActions( IReadOnlyList evaluations, Block block) { @@ -224,11 +219,6 @@ internal long RenderActions( ITrie trie = GetAccountState(block.PreviousHash).Trie; foreach (var evaluation in evaluations) { - if (evaluation.InputContext.BlockAction && Policy.BlockAction is null) - { - continue; - } - ITrie nextTrie = trie; foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) { @@ -287,7 +277,6 @@ internal long RenderActions( block.Index, block.Hash, stopwatch.ElapsedMilliseconds); - return count; } /// From e620c2576650aec49421bd8407cedc6e01ab41b9 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 22 Sep 2023 12:27:27 +0900 Subject: [PATCH 28/31] Renamed for better consistency; added ICommittedActionEvaluation --- ...erContext.cs => CommittedActionContext.cs} | 24 ++++---- Libplanet.Action/CommittedActionEvaluation.cs | 60 +++++++++++++++++++ ...rContext.cs => ICommittedActionContext.cs} | 2 +- .../ICommittedActionEvaluation.cs | 35 +++++++++++ .../Renderers/AnonymousActionRendererTest.cs | 8 +-- .../Renderers/LoggedActionRendererTest.cs | 8 +-- Libplanet/Blockchain/BlockChain.Swap.cs | 4 +- .../Renderers/AnonymousActionRenderer.cs | 14 ++--- .../Renderers/AtomicActionRenderer.cs | 10 ++-- .../Debug/RecordingActionRenderer.cs | 4 +- .../Renderers/Debug/RenderRecord.cs | 8 +-- .../Debug/ValidatingActionRenderer.cs | 4 +- .../Blockchain/Renderers/IActionRenderer.cs | 4 +- .../Renderers/LoggedActionRenderer.cs | 6 +- 14 files changed, 143 insertions(+), 48 deletions(-) rename Libplanet.Action/{ActionRenderContext.cs => CommittedActionContext.cs} (69%) create mode 100644 Libplanet.Action/CommittedActionEvaluation.cs rename Libplanet.Action/{IActionRenderContext.cs => ICommittedActionContext.cs} (98%) create mode 100644 Libplanet.Action/ICommittedActionEvaluation.cs diff --git a/Libplanet.Action/ActionRenderContext.cs b/Libplanet.Action/CommittedActionContext.cs similarity index 69% rename from Libplanet.Action/ActionRenderContext.cs rename to Libplanet.Action/CommittedActionContext.cs index 8857356f2c1..0979257f950 100644 --- a/Libplanet.Action/ActionRenderContext.cs +++ b/Libplanet.Action/CommittedActionContext.cs @@ -6,9 +6,9 @@ namespace Libplanet.Action { - public class ActionRenderContext : IActionRenderContext + public class CommittedActionContext : ICommittedActionContext { - public ActionRenderContext(IActionContext context) + public CommittedActionContext(IActionContext context) : this( signer: context.Signer, txId: context.TxId, @@ -22,7 +22,7 @@ public ActionRenderContext(IActionContext context) { } - public ActionRenderContext( + public CommittedActionContext( Address signer, TxId? txId, Address miner, @@ -44,38 +44,38 @@ public ActionRenderContext( BlockAction = blockAction; } - /// + /// [Pure] public Address Signer { get; } - /// + /// [Pure] public TxId? TxId { get; } - /// + /// [Pure] public Address Miner { get; } - /// + /// [Pure] public long BlockIndex { get; } - /// + /// [Pure] public int BlockProtocolVersion { get; } - /// + /// [Pure] public bool Rehearsal { get; } - /// + /// [Pure] public HashDigest PreviousState { get; } - /// + /// public IRandom Random { get; } - /// + /// [Pure] public bool BlockAction { get; } } diff --git a/Libplanet.Action/CommittedActionEvaluation.cs b/Libplanet.Action/CommittedActionEvaluation.cs new file mode 100644 index 00000000000..6f7e04ff10d --- /dev/null +++ b/Libplanet.Action/CommittedActionEvaluation.cs @@ -0,0 +1,60 @@ +using System; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Common; + +namespace Libplanet.Action +{ + /// + /// A record type to represent an evaluation plan and result of + /// a single action. + /// + public class CommittedActionEvaluation : ICommittedActionEvaluation + { + /// + /// Creates an instance + /// with filling properties. + /// + /// An action to evaluate. + /// An input to + /// evaluate . + /// The result states that + /// makes. + /// An exception that has risen during evaluating a given + /// . + public CommittedActionEvaluation( + IValue action, + ICommittedActionContext inputContext, + HashDigest outputState, + Exception? exception = null) + { + Action = action; + InputContext = inputContext; + OutputState = outputState; + Exception = exception; + } + + /// + /// An action to evaluate. + /// + public IValue Action { get; } + + /// + /// An input to evaluate + /// . + /// + /// Its property + /// is not consumed yet. + public ICommittedActionContext InputContext { get; } + + /// + /// The result states that makes. + /// + public HashDigest OutputState { get; } + + /// + /// An exception that had risen during evaluation. + /// + public Exception? Exception { get; } + } +} diff --git a/Libplanet.Action/IActionRenderContext.cs b/Libplanet.Action/ICommittedActionContext.cs similarity index 98% rename from Libplanet.Action/IActionRenderContext.cs rename to Libplanet.Action/ICommittedActionContext.cs index 42a32aaf1c0..6ec79df4bf7 100644 --- a/Libplanet.Action/IActionRenderContext.cs +++ b/Libplanet.Action/ICommittedActionContext.cs @@ -10,7 +10,7 @@ namespace Libplanet.Action /// /// Contextual data determined by a transaction and a block for rendering. /// - public interface IActionRenderContext + public interface ICommittedActionContext { /// /// The of the that contains diff --git a/Libplanet.Action/ICommittedActionEvaluation.cs b/Libplanet.Action/ICommittedActionEvaluation.cs new file mode 100644 index 00000000000..3fea4f8d9d8 --- /dev/null +++ b/Libplanet.Action/ICommittedActionEvaluation.cs @@ -0,0 +1,35 @@ +using System; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Common; + +namespace Libplanet.Action +{ + public interface ICommittedActionEvaluation + { + /// + /// An action data to evaluate. When the + /// . is true, + /// use instead of trying deserialization. + /// + public IValue Action { get; } + + /// + /// An input to evaluate + /// . + /// + /// Its property + /// is not consumed yet. + public ICommittedActionContext InputContext { get; } + + /// + /// The result states that makes. + /// + public HashDigest OutputState { get; } + + /// + /// An exception that had risen during evaluation. + /// + public Exception? Exception { get; } + } +} diff --git a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs index a6b818fb7a0..1966eb0cf09 100644 --- a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs @@ -18,8 +18,8 @@ public class AnonymousActionRendererTest private static IAccount _account = new Account(MockAccountState.Empty); - private static IActionRenderContext _actionContext = - new ActionRenderContext(new ActionContext( + private static ICommittedActionContext _actionContext = + new CommittedActionContext(new ActionContext( default, default, default, @@ -43,7 +43,7 @@ public class AnonymousActionRendererTest [Fact] public void ActionRenderer() { - (IValue, IActionRenderContext, HashDigest)? record = null; + (IValue, ICommittedActionContext, HashDigest)? record = null; var renderer = new AnonymousActionRenderer { ActionRenderer = (action, context, nextState) => @@ -65,7 +65,7 @@ record = (action, context, nextState), [Fact] public void ActionErrorRenderer() { - (IValue, IActionRenderContext, Exception)? record = null; + (IValue, ICommittedActionContext, Exception)? record = null; var renderer = new AnonymousActionRenderer { ActionErrorRenderer = (action, context, exception) => diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index 593351f5006..4c8220d8616 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -68,8 +68,8 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) { bool called = false; LogEvent firstLog = null; - IActionRenderContext actionContext = - new ActionRenderContext(new ActionContext( + ICommittedActionContext actionContext = + new CommittedActionContext(new ActionContext( default, default, default, @@ -83,7 +83,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) IActionRenderer actionRenderer; if (error) { - Action render = (action, cxt, e) => + Action render = (action, cxt, e) => { LogEvent[] logs = LogEvents.ToArray(); Assert.Single(logs); @@ -104,7 +104,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) } else { - Action> render = + Action> render = (action, cxt, next) => { LogEvent[] logs = LogEvents.ToArray(); diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 5ee46a00c25..9008bebe9ad 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -233,7 +233,7 @@ internal void RenderActions( { renderer.RenderAction( evaluation.Action, - new ActionRenderContext( + new CommittedActionContext( signer: evaluation.InputContext.Signer, txId: evaluation.InputContext.TxId, miner: evaluation.InputContext.Miner, @@ -249,7 +249,7 @@ internal void RenderActions( { renderer.RenderActionError( evaluation.Action, - new ActionRenderContext( + new CommittedActionContext( signer: evaluation.InputContext.Signer, txId: evaluation.InputContext.TxId, miner: evaluation.InputContext.Miner, diff --git a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs index 9b95cac7fa4..42fd7be8aa0 100644 --- a/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AnonymousActionRenderer.cs @@ -30,16 +30,16 @@ public sealed class AnonymousActionRenderer : AnonymousRenderer, IActionRenderer { /// /// A callback function to be invoked together with - /// . + /// . /// - public Action>? ActionRenderer + public Action>? ActionRenderer { get; set; } /// /// A callback function to be invoked together with - /// . + /// . /// - public Action? ActionErrorRenderer { get; set; } + public Action? ActionErrorRenderer { get; set; } /// /// A callback function to be invoked together with @@ -50,16 +50,16 @@ public Action>? ActionRenderer /// public void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState ) => ActionRenderer?.Invoke(action, context, nextState); /// + /// cref="IActionRenderer.RenderActionError(IValue, ICommittedActionContext, Exception)"/> public void RenderActionError( IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception) => ActionErrorRenderer?.Invoke(action, context, exception); diff --git a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs index 646bf8f001f..7b4b7608dcd 100644 --- a/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/AtomicActionRenderer.cs @@ -21,7 +21,7 @@ namespace Libplanet.Blockchain.Renderers /// public sealed class AtomicActionRenderer : IActionRenderer { - private readonly List<(IValue, IActionRenderContext, HashDigest)> _eventBuffer; + private readonly List<(IValue, ICommittedActionContext, HashDigest)> _eventBuffer; private TxId? _lastTxId; private bool _errored; @@ -36,7 +36,7 @@ public AtomicActionRenderer(IActionRenderer actionRenderer) { ActionRenderer = actionRenderer; _lastTxId = null; - _eventBuffer = new List<(IValue, IActionRenderContext, HashDigest)>(); + _eventBuffer = new List<(IValue, ICommittedActionContext, HashDigest)>(); _errored = false; } @@ -62,7 +62,7 @@ public void RenderBlockEnd(Block oldTip, Block newTip) /// public void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState ) { @@ -84,7 +84,7 @@ HashDigest nextState /// public void RenderActionError( IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception) { if (!context.TxId.Equals(_lastTxId)) @@ -104,7 +104,7 @@ public void RenderActionError( private void FlushBuffer( TxId? newTxId, - Action> render + Action> render ) { if (!_errored) diff --git a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs index e04ace87f64..731860a599f 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RecordingActionRenderer.cs @@ -60,7 +60,7 @@ public void ResetRecords() /// public virtual void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState ) { @@ -80,7 +80,7 @@ HashDigest nextState /// public virtual void RenderActionError( IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception ) => _records.Add( diff --git a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs index 78d12a04e39..3c269daa5b2 100644 --- a/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs +++ b/Libplanet/Blockchain/Renderers/Debug/RenderRecord.cs @@ -40,7 +40,7 @@ protected ActionBase( long index, string stackTrace, IValue action, - IActionRenderContext context, + ICommittedActionContext context, bool unrender = false ) : base(index, stackTrace) @@ -58,7 +58,7 @@ protected ActionBase( /// /// The action evaluation context. /// - public IActionRenderContext Context { get; } + public ICommittedActionContext Context { get; } /// /// Whether it is not an unrender event, but a render event. @@ -94,7 +94,7 @@ public ActionSuccess( long index, string stackTrace, IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState, bool unrender = false ) @@ -130,7 +130,7 @@ public ActionError( long index, string stackTrace, IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception, bool unrender = false ) diff --git a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs index f8be6ab7f24..11a4c32a35e 100644 --- a/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/Debug/ValidatingActionRenderer.cs @@ -44,7 +44,7 @@ public override void RenderBlock(Block oldTip, Block newTip) /// public override void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState ) { @@ -55,7 +55,7 @@ HashDigest nextState /// public override void RenderActionError( IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception ) { diff --git a/Libplanet/Blockchain/Renderers/IActionRenderer.cs b/Libplanet/Blockchain/Renderers/IActionRenderer.cs index dce1073cabe..90c9c8c2ab2 100644 --- a/Libplanet/Blockchain/Renderers/IActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/IActionRenderer.cs @@ -66,7 +66,7 @@ public interface IActionRenderer : IRenderer /// void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState); /// @@ -86,7 +86,7 @@ void RenderAction( /// (where its second parameter newTip contains a transaction the belongs to). /// - void RenderActionError(IValue action, IActionRenderContext context, Exception exception); + void RenderActionError(IValue action, ICommittedActionContext context, Exception exception); /// /// Does things that should be done right all actions in a new are diff --git a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs index b0c715bbe43..be0220f80aa 100644 --- a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs @@ -71,7 +71,7 @@ Block newTip /// public void RenderAction( IValue action, - IActionRenderContext context, + ICommittedActionContext context, HashDigest nextState ) => LogActionRendering( @@ -84,7 +84,7 @@ HashDigest nextState /// public void RenderActionError( IValue action, - IActionRenderContext context, + ICommittedActionContext context, Exception exception ) => LogActionRendering( @@ -97,7 +97,7 @@ Exception exception private void LogActionRendering( string methodName, IValue action, - IActionRenderContext context, + ICommittedActionContext context, System.Action callback ) { From 7d8a429dd1603b6613f5c079c27d5d4f1d16cc5b Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 22 Sep 2023 14:25:10 +0900 Subject: [PATCH 29/31] Combined triple commit into one --- Libplanet.Action/ActionContext.cs | 1 + Libplanet.Action/CommittedActionContext.cs | 14 +++ Libplanet.Action/ICommittedActionContext.cs | 2 + Libplanet.Action/Random.cs | 3 - Libplanet.Net/Consensus/Context.cs | 15 +-- .../Blockchain/BlockChainTest.Append.cs | 3 +- Libplanet/Blockchain/BlockChain.Evaluate.cs | 95 +++++++++++++------ Libplanet/Blockchain/BlockChain.Swap.cs | 51 +++------- .../Blockchain/BlockChain.TxExecution.cs | 46 +++------ Libplanet/Blockchain/BlockChain.Validate.cs | 2 +- Libplanet/Blockchain/BlockChain.cs | 2 +- 11 files changed, 121 insertions(+), 113 deletions(-) diff --git a/Libplanet.Action/ActionContext.cs b/Libplanet.Action/ActionContext.cs index 4c0b3bfce21..00ef9fe365b 100644 --- a/Libplanet.Action/ActionContext.cs +++ b/Libplanet.Action/ActionContext.cs @@ -69,6 +69,7 @@ public ActionContext( /// public void UseGas(long gas) => GetGasMeter.Value?.UseGas(gas); + /// [Pure] public IActionContext GetUnconsumedContext() => new ActionContext( diff --git a/Libplanet.Action/CommittedActionContext.cs b/Libplanet.Action/CommittedActionContext.cs index 0979257f950..70a6e23aa1f 100644 --- a/Libplanet.Action/CommittedActionContext.cs +++ b/Libplanet.Action/CommittedActionContext.cs @@ -78,5 +78,19 @@ public CommittedActionContext( /// [Pure] public bool BlockAction { get; } + + /// + [Pure] + public ICommittedActionContext Copy() => + new CommittedActionContext( + Signer, + TxId, + Miner, + BlockIndex, + BlockProtocolVersion, + Rehearsal, + PreviousState, + new Random(Random.Seed), + BlockAction); } } diff --git a/Libplanet.Action/ICommittedActionContext.cs b/Libplanet.Action/ICommittedActionContext.cs index 6ec79df4bf7..3d24518ebc8 100644 --- a/Libplanet.Action/ICommittedActionContext.cs +++ b/Libplanet.Action/ICommittedActionContext.cs @@ -82,5 +82,7 @@ public interface ICommittedActionContext /// [Pure] bool BlockAction { get; } + + ICommittedActionContext Copy(); } } diff --git a/Libplanet.Action/Random.cs b/Libplanet.Action/Random.cs index 294611db661..a5f974322b7 100644 --- a/Libplanet.Action/Random.cs +++ b/Libplanet.Action/Random.cs @@ -1,6 +1,3 @@ -#nullable disable -using System; - namespace Libplanet.Action { internal class Random : System.Random, IRandom diff --git a/Libplanet.Net/Consensus/Context.cs b/Libplanet.Net/Consensus/Context.cs index a6492264786..e9177de1ca2 100644 --- a/Libplanet.Net/Consensus/Context.cs +++ b/Libplanet.Net/Consensus/Context.cs @@ -96,7 +96,9 @@ public partial class Context : IDisposable private readonly ILogger _logger; private readonly - LRUCache EvaluatedActions)> + LRUCache< + BlockHash, + (bool IsValid, IReadOnlyList EvaluatedActions)> _blockValidationCache; private Block? _lockedValue; @@ -191,7 +193,7 @@ private Context( _validatorSet = validators; _cancellationTokenSource = new CancellationTokenSource(); _blockValidationCache = - new LRUCache)>( + new LRUCache)>( cacheSize, Math.Max(cacheSize / 64, 8)); _contextTimeoutOption = contextTimeoutOptions ?? new ContextTimeoutOption(); @@ -454,7 +456,8 @@ private void PublishMessage(ConsensusMsg message) /// /// if block is valid, otherwise . /// - private bool IsValid(Block block, out IReadOnlyList evaluatedActions) + private bool IsValid( + Block block, out IReadOnlyList evaluatedActions) { if (_blockValidationCache.TryGet(block.Hash, out var cached)) { @@ -466,11 +469,11 @@ private bool IsValid(Block block, out IReadOnlyList evaluated // Need to get txs from store, lock? // TODO: Remove ChainId, enhancing lock management. _blockChain._rwlock.EnterUpgradeableReadLock(); - IReadOnlyList actionEvaluations; + IReadOnlyList actionEvaluations; if (block.Index != Height) { - evaluatedActions = ImmutableArray.Empty; + evaluatedActions = ImmutableArray.Empty; _blockValidationCache.AddReplace(block.Hash, (false, evaluatedActions)); return false; } @@ -515,7 +518,7 @@ e is InvalidTxException || "Block #{Index} {Hash} is invalid", block.Index, block.Hash); - evaluatedActions = ImmutableArray.Empty; + evaluatedActions = ImmutableArray.Empty; _blockValidationCache.AddReplace(block.Hash, (false, evaluatedActions)); return false; } diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index f9aa4c2a87c..ef4f31e9d37 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -610,7 +610,8 @@ public void CachedActionEvaluationWrittenOnAppend() _blockChain.StageTransaction(txA0); _blockChain.StageTransaction(txA1); Block block = _blockChain.ProposeBlock(miner); - IReadOnlyList actionEvaluations = _blockChain.EvaluateBlock(block); + (IReadOnlyList actionEvaluations, _) = + _blockChain.ToCommittedEvaluation(block, _blockChain.EvaluateBlock(block)); Assert.Equal(0L, _blockChain.Tip.Index); _blockChain.Append( block, diff --git a/Libplanet/Blockchain/BlockChain.Evaluate.cs b/Libplanet/Blockchain/BlockChain.Evaluate.cs index 5efc9112965..e8bc389fc07 100644 --- a/Libplanet/Blockchain/BlockChain.Evaluate.cs +++ b/Libplanet/Blockchain/BlockChain.Evaluate.cs @@ -7,6 +7,7 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; @@ -97,44 +98,27 @@ public static IReadOnlyList EvaluateGenesis( /// /// public HashDigest DetermineBlockStateRootHash( - IPreEvaluationBlock block, out IReadOnlyList evaluations) + IPreEvaluationBlock block, out IReadOnlyList evaluations) { _rwlock.EnterWriteLock(); try { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - evaluations = EvaluateBlock(block); - var totalDelta = evaluations.GetRawTotalDelta(); + var rawEvaluations = EvaluateBlock(block); + _logger.Debug( - "Took {DurationMs} ms to summarize the states delta with {KeyCount} key " + - "changes made by block #{BlockIndex} pre-evaluation hash {PreEvaluationHash}", + "Took {DurationMs} ms to evaluate block #{BlockIndex} " + + "pre-evaluation hash {PreEvaluationHash} with {Count} action evaluations", stopwatch.ElapsedMilliseconds, - totalDelta.Count, block.Index, - block.PreEvaluationHash); - - ITrie trie = GetAccountState(block.PreviousHash).Trie; - foreach (var kv in totalDelta) - { - trie = trie.Set(kv.Key, kv.Value); - } + block.PreEvaluationHash, + rawEvaluations.Count); - trie = StateStore.Commit(trie); - HashDigest rootHash = trie.Hash; - _logger - .ForContext("Tag", "Metric") - .ForContext("Subtag", "StateUpdateDuration") - .Information( - "Took {DurationMs} ms to update the states with {KeyCount} key changes " + - "and resulting in state root hash {StateRootHash} for " + - "block #{BlockIndex} pre-evaluation hash {PreEvaluationHash}", - stopwatch.ElapsedMilliseconds, - totalDelta.Count, - rootHash, - block.Index, - block.PreEvaluationHash); + (var committedEvaluations, var rootHash) = + ToCommittedEvaluation(block, rawEvaluations); + evaluations = committedEvaluations; return rootHash; } finally @@ -183,5 +167,62 @@ internal Block EvaluateAndSign( : throw new ArgumentException( $"Given {nameof(preEvaluationBlock)} must have protocol version " + $"2 or greater: {preEvaluationBlock.ProtocolVersion}"); + + internal (IReadOnlyList, HashDigest) + ToCommittedEvaluation( + IPreEvaluationBlock block, + IReadOnlyList evaluations) + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + ITrie trie = GetAccountState(block.PreviousHash).Trie; + var committedEvaluations = new List(); + + int setCount = 0; + foreach (var evaluation in evaluations) + { + ITrie nextTrie = trie; + foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) + { + nextTrie = nextTrie.Set(kv.Key, kv.Value); + setCount++; + } + + nextTrie = StateStore.Commit(nextTrie); + var committedEvaluation = new CommittedActionEvaluation( + action: evaluation.Action, + inputContext: new CommittedActionContext( + signer: evaluation.InputContext.Signer, + txId: evaluation.InputContext.TxId, + miner: evaluation.InputContext.Miner, + blockIndex: evaluation.InputContext.BlockIndex, + blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, + rehearsal: evaluation.InputContext.Rehearsal, + previousState: trie.Hash, + random: evaluation.InputContext.GetUnconsumedContext().Random, + blockAction: evaluation.InputContext.BlockAction), + outputState: nextTrie.Hash, + exception: evaluation.Exception); + committedEvaluations.Add(committedEvaluation); + + trie = nextTrie; + } + + _logger + .ForContext("Tag", "Metric") + .ForContext("Subtag", "StateUpdateDuration") + .Information( + "Took {DurationMs} ms to update the states with {Count} key changes " + + "and resulting in state root hash {StateRootHash} for " + + "block #{BlockIndex} pre-evaluation hash {PreEvaluationHash}", + stopwatch.ElapsedMilliseconds, + setCount, + trie.Hash, + block.Index, + block.PreEvaluationHash); + + return (committedEvaluations, trie.Hash); + } } } diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 9008bebe9ad..b9330f1cb4b 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -5,10 +5,8 @@ using System.Diagnostics; using System.Linq; using Libplanet.Action; -using Libplanet.Action.State; using Libplanet.Blockchain.Renderers; using Libplanet.Store; -using Libplanet.Store.Trie; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -175,9 +173,10 @@ internal void RenderFastForward( Block block = Store.GetBlock(hash); ImmutableList evaluations = ActionEvaluator.Evaluate(block).ToImmutableList(); + (var committedEvaluations, _) = ToCommittedEvaluation(block, evaluations); RenderActions( - evaluations: evaluations, + evaluations: committedEvaluations, block: block); } @@ -200,9 +199,14 @@ internal void RenderFastForward( /// , evaluate actions of the again. /// to render actions. internal void RenderActions( - IReadOnlyList evaluations, + IReadOnlyList evaluations, Block block) { + if (evaluations is null) + { + throw new NullReferenceException(nameof(evaluations)); + } + Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); _logger.Debug( @@ -210,59 +214,26 @@ internal void RenderActions( block.Index, block.Hash); - if (evaluations is null) - { - evaluations = ActionEvaluator.Evaluate(block); - } - long count = 0; - ITrie trie = GetAccountState(block.PreviousHash).Trie; foreach (var evaluation in evaluations) { - ITrie nextTrie = trie; - foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) - { - nextTrie = nextTrie.Set(kv.Key, kv.Value); - } - - nextTrie = StateStore.Commit(nextTrie); - foreach (IActionRenderer renderer in ActionRenderers) { if (evaluation.Exception is null) { renderer.RenderAction( evaluation.Action, - new CommittedActionContext( - signer: evaluation.InputContext.Signer, - txId: evaluation.InputContext.TxId, - miner: evaluation.InputContext.Miner, - blockIndex: evaluation.InputContext.BlockIndex, - blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, - rehearsal: evaluation.InputContext.Rehearsal, - previousState: trie.Hash, - random: evaluation.InputContext.GetUnconsumedContext().Random, - blockAction: evaluation.InputContext.BlockAction), - nextTrie.Hash); + evaluation.InputContext.Copy(), + evaluation.OutputState); } else { renderer.RenderActionError( evaluation.Action, - new CommittedActionContext( - signer: evaluation.InputContext.Signer, - txId: evaluation.InputContext.TxId, - miner: evaluation.InputContext.Miner, - blockIndex: evaluation.InputContext.BlockIndex, - blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, - rehearsal: evaluation.InputContext.Rehearsal, - previousState: trie.Hash, - random: evaluation.InputContext.GetUnconsumedContext().Random, - blockAction: evaluation.InputContext.BlockAction), + evaluation.InputContext.Copy(), evaluation.Exception); } - trie = nextTrie; count++; } } diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index c4d7f19bded..7f381cbc0a9 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -19,17 +19,17 @@ public partial class BlockChain /// The corresponding s. internal IEnumerable MakeTxExecutions( Block block, - IReadOnlyList evaluations + IReadOnlyList evaluations ) { - List<(TxId?, List)> groupedEvals = - new List<(TxId?, List)>(); - foreach (IActionEvaluation eval in evaluations) + List<(TxId?, List)> groupedEvals = + new List<(TxId?, List)>(); + foreach (ICommittedActionEvaluation eval in evaluations) { if (groupedEvals.Count == 0) { groupedEvals.Add( - (eval.InputContext.TxId, new List() { eval })); + (eval.InputContext.TxId, new List() { eval })); } else { @@ -40,7 +40,10 @@ IReadOnlyList evaluations else { groupedEvals.Add( - (eval.InputContext.TxId, new List() { eval })); + ( + eval.InputContext.TxId, + new List() { eval } + )); } } } @@ -52,17 +55,7 @@ IReadOnlyList evaluations { if (group.Item1 is { } txId) { - ITrie nextTrie = trie; - foreach (var eval in group.Item2) - { - foreach (var kv in eval.OutputState.Delta.ToRawDelta()) - { - nextTrie = nextTrie.Set(kv.Key, kv.Value); - } - } - - nextTrie = StateStore.Commit(nextTrie); - + // If txId is not null, group has at least one element. List exceptions = group.Item2 .Select(eval => eval.Exception) .Select(exception => exception is { } e && e.InnerException is { } i @@ -74,26 +67,11 @@ IReadOnlyList evaluations block.Hash, txId, exceptions.Any(exception => exception is { }), - trie.Hash, - nextTrie.Hash, + group.Item2.First().InputContext.PreviousState, + group.Item2.Last().OutputState, exceptions.ToList()); count++; - trie = nextTrie; - } - else - { - ITrie nextTrie = trie; - foreach (var eval in group.Item2) - { - foreach (var kv in eval.OutputState.Delta.ToRawDelta()) - { - nextTrie = nextTrie.Set(kv.Key, kv.Value); - } - } - - nextTrie = StateStore.Commit(nextTrie); - trie = nextTrie; } } diff --git a/Libplanet/Blockchain/BlockChain.Validate.cs b/Libplanet/Blockchain/BlockChain.Validate.cs index 4242a8f5a43..3e008b8ad71 100644 --- a/Libplanet/Blockchain/BlockChain.Validate.cs +++ b/Libplanet/Blockchain/BlockChain.Validate.cs @@ -318,7 +318,7 @@ internal void ValidateBlock(Block block) /// /// internal void ValidateBlockStateRootHash( - Block block, out IReadOnlyList evaluations) + Block block, out IReadOnlyList evaluations) { var rootHash = DetermineBlockStateRootHash(block, out evaluations); if (!rootHash.Equals(block.StateRootHash)) diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 2b48c883a2a..76591f08835 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -964,7 +964,7 @@ internal void Append( Block block, BlockCommit blockCommit, bool render, - IReadOnlyList actionEvaluations = null + IReadOnlyList actionEvaluations = null ) { if (Count == 0) From fde8c9524151e1a5467f2edf2ec72f80fc852502 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 22 Sep 2023 17:24:04 +0900 Subject: [PATCH 30/31] Changelog --- CHANGES.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index aca7e306747..b6a029b6280 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,12 +12,6 @@ To be released. - Added `IBlockChainStates.GetAccountState(HashDigest?)` interface method. [[#3425]] - - Changed `IActionRenderer.RenderAction(IValue, IActionContext, IAccount)` - to `IActionRenderer.RenderAction(IValue, IActionRenderContext, - HashDigest)`. [[#3427]] - - Changed `IActionRenderer.RenderActionError(IValue, IActionContext, - Exception)` to `IActionRenderer.RenderActionError(IValue, - IActionRenderContext, Exception)`. [[#3427]] - Removed `TxFailure.ExceptionMetadata` property. [[#3428]] - Removed `ISerializable` interface from `TxExecution`, `TxSuccess`, and `TxFailure`. [[#3428]] @@ -32,6 +26,12 @@ To be released. and added `TxResult.ExceptionNames` of type `List?`. [[#3429]] - (Libplanet.Explorer) Removed `TxResult.UpdatedStates` and `TxResult.UpdatedFungibleAssets`. [[#3429]] + - Changed `IActionRenderer.RenderAction(IValue, IActionContext, IAccount)` + to `IActionRenderer.RenderAction(IValue, ICommittedActionContext, + HashDigest)`. [[#3431]] + - Changed `IActionRenderer.RenderActionError(IValue, IActionContext, + Exception)` to `IActionRenderer.RenderActionError(IValue, + ICommittedActionContext, Exception)`. [[#3431]] ### Backward-incompatible network protocol changes @@ -40,7 +40,8 @@ To be released. ### Added APIs - Added `AccountDiff` class. [[#3424]] - - Added `IActionRenderContext` interface. [[#3427]] + - Added `ICommittedActionContext` interface. [[#3431]] + - Added `ICommittedActionEvaluation` interface. [[#3431]] ### Behavioral changes @@ -52,9 +53,9 @@ To be released. [#3424]: https://github.com/planetarium/libplanet/pull/3424 [#3425]: https://github.com/planetarium/libplanet/pull/3425 -[#3427]: https://github.com/planetarium/libplanet/pull/3427 [#3428]: https://github.com/planetarium/libplanet/pull/3428 [#3429]: https://github.com/planetarium/libplanet/pull/3429 +[#3431]: https://github.com/planetarium/libplanet/pull/3431 Version 3.3.1 From ec42f31dd8115c2489dbc734b4c3c34f0b416a2f Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Mon, 25 Sep 2023 16:39:17 +0900 Subject: [PATCH 31/31] Docs fix --- Libplanet.Store/IStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libplanet.Store/IStore.cs b/Libplanet.Store/IStore.cs index a2d39af0f22..bbcbde2fd7e 100644 --- a/Libplanet.Store/IStore.cs +++ b/Libplanet.Store/IStore.cs @@ -162,13 +162,13 @@ public interface IStore : IDisposable bool ContainsBlock(BlockHash blockHash); /// - /// Records the given . + /// Records the given . /// /// If there is already the record for the same /// and , the record is silently overwritten. /// The transaction execution summary to record. /// Must not be . - /// Thrown when is + /// Thrown when is /// . /// void PutTxExecution(TxExecution txExecution);