Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
UnexpectedlyTerminatedActionException
  • Loading branch information
dahlia committed Sep 4, 2019
1 parent 5aa9c2d commit 11132c6
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 73 deletions.
10 changes: 10 additions & 0 deletions CHANGES.md
Expand Up @@ -8,6 +8,16 @@ To be released.

### Backward-incompatible interface changes

- Replaced `UnexpectedlyTerminatedTxRehearsalException` with
`UnexpectedlyTerminatedActionException`.
- The following methods became to throw
`UnexpectedlyTerminatedActionException` with having its `InnerException`
during actions being evaluated if any action of them throws an exception:
- `Transaction<T>.EvaluateActions()`
- `Transaction<T>.EvaluateActionsGradually()`
- `Block<T>.EvaluateActionsPerTx()`
- `Block<T>.Evaluate()`
- `BlockChain<T>.GetStates(completeStates: true)`
- The concept of "namespaces" in `IStore` was replaced by "chain IDs"
to be consistent with `BlockChain<T>`. [[#483], [#486]]
- Renamed `IStore.ListNamespaces()` method to `ListChainIds()`.
Expand Down
10 changes: 6 additions & 4 deletions Libplanet.Tests/Common/Action/ThrowException.cs
Expand Up @@ -11,22 +11,24 @@ public ThrowException()
{
}

public bool Throw { get; set; } = false;
public bool ThrowOnRehearsal { get; set; } = false;

public bool ThrowOnExecution { get; set; } = false;

public IImmutableDictionary<string, object> PlainValue =>
new Dictionary<string, object>()
{
{ "throw", Throw },
{ "throw", ThrowOnRehearsal },
}.ToImmutableDictionary();

public void LoadPlainValue(IImmutableDictionary<string, object> plainValue)
{
Throw = (bool)plainValue["throw"];
ThrowOnRehearsal = (bool)plainValue["throw"];
}

public IAccountStateDelta Execute(IActionContext context)
{
if (Throw)
if (context.Rehearsal ? ThrowOnRehearsal : ThrowOnExecution)
{
throw new SomeException("An expected exception.");
}
Expand Down
56 changes: 46 additions & 10 deletions Libplanet.Tests/Tx/TransactionTest.cs
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.Tests.Common.Action;
Expand Down Expand Up @@ -171,16 +172,22 @@ public void CreateWithMissingRequiredArguments()
[Fact]
public void CreateWithActionsThrowingException()
{
var action = new ThrowException { Throw = true };
Assert.Throws<UnexpectedlyTerminatedTxRehearsalException>(() =>
Transaction<ThrowException>.Create(
0,
_fx.PrivateKey1,
new[] { action },
ImmutableHashSet<Address>.Empty,
DateTimeOffset.UtcNow
)
);
var action = new ThrowException { ThrowOnRehearsal = true };
UnexpectedlyTerminatedActionException e =
Assert.Throws<UnexpectedlyTerminatedActionException>(() =>
Transaction<ThrowException>.Create(
0,
_fx.PrivateKey1,
new[] { action },
ImmutableHashSet<Address>.Empty,
DateTimeOffset.UtcNow
)
);
Assert.Null(e.BlockHash);
Assert.Null(e.BlockIndex);
Assert.Null(e.TxId);
Assert.Same(action, e.Action);
Assert.IsType<ThrowException.SomeException>(e.InnerException);
}

[Fact]
Expand Down Expand Up @@ -715,6 +722,35 @@ public void EvaluateActions()
}
}

[Fact]
public void EvaluateActionsThrowingException()
{
var action = new ThrowException { ThrowOnRehearsal = false, ThrowOnExecution = true };
Transaction<ThrowException> tx = Transaction<ThrowException>.Create(
0,
_fx.PrivateKey1,
new[] { action },
ImmutableHashSet<Address>.Empty,
DateTimeOffset.UtcNow
);
var hash = new HashDigest<SHA256>(GetRandomBytes(HashDigest<SHA256>.Size));
UnexpectedlyTerminatedActionException e =
Assert.Throws<UnexpectedlyTerminatedActionException>(() =>
tx.EvaluateActions(
blockHash: hash,
blockIndex: 123,
previousStates: new AccountStateDeltaImpl(_ => null),
minerAddress: GenesisMinerAddress,
rehearsal: false
)
);
Assert.Equal(hash, e.BlockHash);
Assert.Equal(123, e.BlockIndex);
Assert.Equal(tx.Id, e.TxId);
Assert.IsType<ThrowException>(e.Action);
Assert.IsType<ThrowException.SomeException>(e.InnerException);
}

[Fact]
public void Validate()
{
Expand Down
37 changes: 22 additions & 15 deletions Libplanet/Action/ActionEvaluation.cs
Expand Up @@ -3,6 +3,8 @@
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Blockchain.Policies;
using Libplanet.Blocks;
using Libplanet.Tx;

namespace Libplanet.Action
Expand Down Expand Up @@ -55,12 +57,13 @@ IAccountStateDelta outputStates
/// Executes the <paramref name="actions"/> step by step, and emits
/// <see cref="ActionEvaluation"/> for each step.
/// </summary>
/// <param name="blockHash">The <see cref="Libplanet.Blocks.Block{T}.Hash"/> of
/// <see cref="Libplanet.Blocks.Block{T}"/> that this <see cref="Transaction{T}"/> will
/// belong to.</param>
/// <param name="blockIndex">The <see cref="Libplanet.Blocks.Block{T}.Index"/> of
/// <see cref="Libplanet.Blocks.Block{T}"/> that this <see cref="Transaction{T}"/> will
/// belong to.</param>
/// <param name="blockHash">The <see cref="Block{T}.Hash"/> of <see cref="Block{T}"/> that
/// <paramref name="actions"/> belongs to.</param>
/// <param name="blockIndex">The <see cref="Block{T}.Index"/> of <see cref="Block{T}"/> that
/// <paramref name="actions"/> belongs to.</param>
/// <param name="txid">The <see cref="Transaction{T}.Id"/> of <see cref="Transaction{T}"/>
/// that <paramref name="actions"/> belongs to. This can be <c>null</c> on rehearsal mode
/// or if an action is a <see cref="IBlockPolicy{T}.BlockAction"/>.</param>
/// <param name="previousStates">The states immediately before <paramref name="actions"/>
/// being executed. Note that its <see cref="IAccountStateDelta.UpdatedAddresses"/> are
/// remained to the returned next states.</param>
Expand All @@ -77,17 +80,15 @@ IAccountStateDelta outputStates
/// Note that each <see cref="IActionContext.Random"/> object
/// has a unconsumed state.
/// </returns>
/// <exception cref="UnexpectedlyTerminatedTxRehearsalException">
/// Thrown when one of <paramref name="actions"/> throws some
/// exception during <paramref name="rehearsal"/> mode.
/// <exception cref="UnexpectedlyTerminatedActionException">
/// Thrown when one of <paramref name="actions"/> throws some exception.
/// The actual exception that an <see cref="IAction"/> threw
/// is stored in its <see cref="Exception.InnerException"/> property.
/// It is never thrown if the <paramref name="rehearsal"/> option is
/// <c>false</c>.
/// </exception>
internal static IEnumerable<ActionEvaluation> EvaluateActionsGradually(
HashDigest<SHA256> blockHash,
long blockIndex,
TxId? txid,
IAccountStateDelta previousStates,
Address minerAddress,
Address signer,
Expand Down Expand Up @@ -123,12 +124,18 @@ int randomSeed
}
catch (Exception e)
{
string msg;
if (!rehearsal)
{
throw;
msg = $"The action {action} (block #{blockIndex} {blockHash}, tx {txid}) " +
"threw an exception during execution. See also this exception's " +
"InnerException property.";
throw new UnexpectedlyTerminatedActionException(
blockHash, blockIndex, txid, action, msg, e
);
}

var msg =
msg =
$"The action {action} threw an exception during its " +
"rehearsal. It is probably because the logic of the " +
$"action {action} is not enough generic so that it " +
Expand All @@ -137,8 +144,8 @@ int randomSeed
"useful to make the action can deal with the case of " +
"rehearsal mode.\n" +
"See also this exception's InnerException property.";
throw new UnexpectedlyTerminatedTxRehearsalException(
action, msg, e
throw new UnexpectedlyTerminatedActionException(
null, null, null, action, msg, e
);
}

Expand Down
75 changes: 75 additions & 0 deletions Libplanet/Action/UnexpectedlyTerminatedActionException.cs
@@ -0,0 +1,75 @@
using System;
using System.Security.Cryptography;
using Libplanet.Blockchain.Policies;
using Libplanet.Blocks;
using Libplanet.Tx;

namespace Libplanet.Action
{
/// <summary>
/// The exception that is thrown during an <see cref="IAction"/> is being evaluated.
/// <para>The actual exception that the <see cref="Action"/> threw
/// is stored in the <see cref="Exception.InnerException"/> property.</para>
/// </summary>
[Serializable]
public sealed class UnexpectedlyTerminatedActionException : Exception
{
/// <summary>
/// Creates a new <see cref="UnexpectedlyTerminatedActionException"/> object.
/// </summary>
/// <param name="blockHash">The <see cref="Block{T}.Hash"/> of the <see cref="Block{T}"/>
/// that <paramref name="action"/> belongs to. This can be <c>null</c> on rehearsal mode.
/// </param>
/// <param name="blockIndex">The <see cref="Block{T}.Index"/> of the <see cref="Block{T}"/>
/// that <paramref name="action"/> belongs to. This can be <c>null</c> on rehearsal mode.
/// </param>
/// <param name="txid">The <see cref="Transaction{T}.Id"/> of
/// the <see cref="Transaction{T}"/> that <paramref name="action"/> belongs to.
/// This can be <c>null</c> on rehearsal mode or if <paramref name="action"/> is
/// a <see cref="IBlockPolicy{T}.BlockAction"/>.
/// </param>
/// <param name="action">The <see cref="IAction"/> object which threw an exception.</param>
/// <param name="message">Specifies a <see cref="Exception.Message"/>.</param>
/// <param name="innerException">The actual exception that the <see cref="Action"/> threw.
/// </param>
public UnexpectedlyTerminatedActionException(
HashDigest<SHA256>? blockHash,
long? blockIndex,
TxId? txid,
IAction action,
string message,
Exception innerException
)
: base(message, innerException)
{
BlockHash = blockHash;
BlockIndex = blockIndex;
TxId = txid;
Action = action;
}

/// <summary>
/// The <see cref="Block{T}.Hash"/> of the <see cref="Block{T}"/> that <see cref="Action"/>
/// belongs to. This can be <c>null</c> on rehearsal mode.
/// </summary>
public HashDigest<SHA256>? BlockHash { get; }

/// <summary>
/// The <see cref="Block{T}.Index"/> of the <see cref="Block{T}"/> that <see cref="Action"/>
/// belongs to. This can be <c>null</c> on rehearsal mode.
/// </summary>
public long? BlockIndex { get; }

/// <summary>
/// The <see cref="Transaction{T}.Id"/> of the <see cref="Transaction{T}"/> that
/// <see cref="Action"/> belongs to. This can be <c>null</c> on rehearsal mode or
/// if <see cref="Action"/> is a <see cref="IBlockPolicy{T}.BlockAction"/>.
/// </summary>
public TxId? TxId { get; }

/// <summary>
/// The <see cref="IAction"/> object which threw an exception.
/// </summary>
public IAction Action { get; }
}
}
1 change: 1 addition & 0 deletions Libplanet/Blockchain/BlockChain.cs
Expand Up @@ -700,6 +700,7 @@ IReadOnlyList<ActionEvaluation> EvaluateActions()
return ActionEvaluation.EvaluateActionsGradually(
block.Hash,
block.Index,
null,
lastStates,
miner,
miner,
Expand Down
7 changes: 4 additions & 3 deletions Libplanet/Tx/Transaction.cs
Expand Up @@ -353,7 +353,7 @@ public static Transaction<T> FromBencodex(byte[] bytes)
/// is passed to <paramref name="privateKey"/> or
/// or <paramref name="actions"/>.
/// </exception>
/// <exception cref="UnexpectedlyTerminatedTxRehearsalException">
/// <exception cref="UnexpectedlyTerminatedActionException">
/// Thrown when one of <paramref name="actions"/> throws some
/// exception during their rehearsal.
/// <para>This exception is thrown probably because the logic of some of
Expand Down Expand Up @@ -503,7 +503,7 @@ public byte[] ToBencodex(bool sign)
/// Note that each <see cref="IActionContext.Random"/> object has
/// a unconsumed state.
/// </returns>
/// <exception cref="UnexpectedlyTerminatedTxRehearsalException">
/// <exception cref="UnexpectedlyTerminatedActionException">
/// Thrown when one of <see cref="Actions"/> throws some
/// exception during <paramref name="rehearsal"/> mode.
/// The actual exception that an <see cref="IAction"/> threw
Expand All @@ -524,6 +524,7 @@ public byte[] ToBencodex(bool sign)
return ActionEvaluation.EvaluateActionsGradually(
blockHash,
blockIndex,
Id,
previousStates,
minerAddress,
Signer,
Expand Down Expand Up @@ -555,7 +556,7 @@ public byte[] ToBencodex(bool sign)
/// being executed. Note that it maintains
/// <see cref="IAccountStateDelta.UpdatedAddresses"/> of the given
/// <paramref name="previousStates"/> as well.</returns>
/// <exception cref="UnexpectedlyTerminatedTxRehearsalException">
/// <exception cref="UnexpectedlyTerminatedActionException">
/// Thrown when one of <see cref="Actions"/> throws some
/// exception during <paramref name="rehearsal"/> mode.
/// The actual exception that an <see cref="IAction"/> threw
Expand Down
41 changes: 0 additions & 41 deletions Libplanet/Tx/UnexpectedlyTerminatedTxRehearsalException.cs

This file was deleted.

0 comments on commit 11132c6

Please sign in to comment.