Skip to content

Commit

Permalink
Merge 067c4ea into d58f92d
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia authored May 3, 2021
2 parents d58f92d + 067c4ea commit 1474b66
Show file tree
Hide file tree
Showing 21 changed files with 896 additions and 13 deletions.
22 changes: 22 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ To be released.
- The type of `TrieStateStore.PruneStates()` method's `excludeBlockHashes`
parameter became `IImmutableSet<BlockHash>`
(was `ImmutableHashSet<HashDigest<SHA256>>`).
- Added `IActionContext.TxId` property. [[#1275]]
- Removed `StunMessage.Parse(Stream)` method. [[#1228]]
- Moved `ITransport` and `NetMQTransport` from `Libplanet.Net` to
`Libplanet.Net.Transports`. [[#1235]]
Expand All @@ -142,6 +143,7 @@ To be released.
- Added `NetMQTransport.QueryAppProtocolVersion()` static method. [[#1235]]
- Added `BoundPeer.Parse()` static method. [[#1240]]
- Added `TransportException` class. [[#1242]]
- Added `AtomicActionRenderer<T>` class. [[#1267], [#1275]]

### Behavioral changes

Expand All @@ -150,6 +152,23 @@ To be released.

### Bug fixes

- Fixed a bug where executing `Transaction<T>.Actions` had not been atomic.
`Actions` in a `Transaction<T>` now became executed all or nothing at all.
[[#1267], [#1275]]
- `Transaction<T>.EvaluateActions()` method became atomic.
- `Transaction<T>.EvaluateActionsGradually()` method had returned
the same number of `ActionEvaluation`s to `Transaction<T>.Actions`,
but now became to omit the evaluations after the first action throwing
an exception. If no action throws any exception, it still returns
the same number of `ActionEvaluation`s to `Transaction<T>.Actions`.
- State-wise, `Transaction<T>`s having any exception-throwing action
now do not commit any changes at all to `IStateStore`.
- Rendering-wise, for actions following the first exception-throwing
action, action rendering methods in `IActionRenderer<T>`
(`RenderAction()`, `RenderActionError()`, `UnrenderAction()`, and
`UnrenderActionError()`) became not invoked.
If you want to dismiss all actions in unsuccessful transactions at all,
wrap your action renderer with `AtomicActionRenderer<T>`.
- Fixed a bug where `KademliaProtocol.BootstrapAsync()` has sent multiple
`Ping` messages to other peers. [[#1219]]
- Fixed a bug where `KademliaProtocol.CheckReplacementCacheAsync()` has
Expand Down Expand Up @@ -182,9 +201,12 @@ To be released.
[#1240]: https://github.com/planetarium/libplanet/pull/1240
[#1242]: https://github.com/planetarium/libplanet/pull/1242
[#1265]: https://github.com/planetarium/libplanet/pull/1265
[#1267]: https://github.com/planetarium/libplanet/issues/1267
[#1268]: https://github.com/planetarium/libplanet/pull/1268
[#1272]: https://github.com/planetarium/libplanet/pull/1272
[#1274]: https://github.com/planetarium/libplanet/pull/1274
[#1275]: https://github.com/planetarium/libplanet/pull/1275



Version 0.11.1
Expand Down
39 changes: 33 additions & 6 deletions Libplanet.Tests/Action/ActionContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Libplanet.Assets;
using Libplanet.Store.Trie;
using Libplanet.Tests.Store.Trie;
using Libplanet.Tx;
using Xunit;

namespace Libplanet.Tests.Action
Expand All @@ -21,10 +22,17 @@ public void RandomShouldBeDeterministic()
(1, 534011718),
};
var address = new Address("21744f4f08db23e044178dafb8273aeb5ebe6644");
var txid = new TxId(new byte[]
{
0xd8, 0x35, 0x6c, 0x35, 0xb8, 0x6e, 0x49, 0xf0, 0x84, 0x3c, 0x9c,
0xe8, 0x26, 0xe1, 0xda, 0x01, 0x4f, 0x7a, 0x1b, 0xa8, 0x28, 0x32,
0x8f, 0x8c, 0x12, 0x32, 0x72, 0x84, 0x00, 0xfc, 0x74, 0x89,
});
foreach (var (seed, expected) in testCases)
{
var context = new ActionContext(
signer: address,
txid: txid,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand All @@ -39,8 +47,16 @@ public void RandomShouldBeDeterministic()
public void GuidShouldBeDeterministic()
{
var address = new Address("21744f4f08db23e044178dafb8273aeb5ebe6644");
var txid = new TxId(new byte[]
{
0xd8, 0x35, 0x6c, 0x35, 0xb8, 0x6e, 0x49, 0xf0, 0x84, 0x3c, 0x9c,
0xe8, 0x26, 0xe1, 0xda, 0x01, 0x4f, 0x7a, 0x1b, 0xa8, 0x28, 0x32,
0x8f, 0x8c, 0x12, 0x32, 0x72, 0x84, 0x00, 0xfc, 0x74, 0x89,
});

var context1 = new ActionContext(
signer: address,
txid: txid,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand All @@ -49,6 +65,7 @@ public void GuidShouldBeDeterministic()

var context2 = new ActionContext(
signer: address,
txid: txid,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand All @@ -57,6 +74,7 @@ public void GuidShouldBeDeterministic()

var context3 = new ActionContext(
signer: address,
txid: txid,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand Down Expand Up @@ -87,16 +105,23 @@ public void GuidShouldBeDeterministic()
public void GuidVersionAndVariant()
{
var address = new Address("21744f4f08db23e044178dafb8273aeb5ebe6644");
var txid = new TxId(new byte[]
{
0xd8, 0x35, 0x6c, 0x35, 0xb8, 0x6e, 0x49, 0xf0, 0x84, 0x3c, 0x9c,
0xe8, 0x26, 0xe1, 0xda, 0x01, 0x4f, 0x7a, 0x1b, 0xa8, 0x28, 0x32,
0x8f, 0x8c, 0x12, 0x32, 0x72, 0x84, 0x00, 0xfc, 0x74, 0x89,
});

for (var i = 0; i < 100; i++)
{
var context = new ActionContext(
signer: address,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
randomSeed: i
);
signer: address,
txid: txid,
miner: address,
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
randomSeed: i
);
var guid = context.Random.GenerateRandomGuid().ToString();

Assert.Equal('4', guid[14]);
Expand All @@ -110,6 +135,7 @@ public void GetUnconsumedContext()
var random = new System.Random();
var original = new ActionContext(
signer: random.NextAddress(),
txid: random.NextTxId(),
miner: random.NextAddress(),
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand Down Expand Up @@ -142,6 +168,7 @@ public void LazyPreviousStateRootHash(bool callPreviousStateRootHash)
var random = new System.Random();
var actionContext = new ActionContext(
signer: random.NextAddress(),
txid: random.NextTxId(),
miner: random.NextAddress(),
blockIndex: 1,
previousStates: new DumbAccountStateDelta(),
Expand Down
145 changes: 145 additions & 0 deletions Libplanet.Tests/Action/ActionEvaluationTest.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Assets;
using Libplanet.Blocks;
using Libplanet.Crypto;
using Libplanet.Tests.Common.Action;
using Libplanet.Tests.Fixtures;
using Libplanet.Tx;
using Serilog;
using Xunit;
using Xunit.Abstractions;

namespace Libplanet.Tests.Action
{
public class ActionEvaluationTest
{
private readonly ILogger _logger;

public ActionEvaluationTest(ITestOutputHelper output)
{
Log.Logger = _logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithThreadId()
.WriteTo.TestOutput(output)
.CreateLogger()
.ForContext<ActionEvaluationTest>();
}

[Fact]
public void Constructor()
{
var txid = new System.Random().NextTxId();
Address address = new PrivateKey().ToAddress();
var evaluation = new ActionEvaluation(
new DumbAction(address, "item"),
new ActionContext(
address,
txid,
address,
1,
new AccountStateDeltaImpl(
Expand All @@ -38,6 +61,7 @@ public void Constructor()
Assert.Equal(address, action.TargetAddress);
Assert.Equal("item", action.Item);
Assert.Equal(address, evaluation.InputContext.Signer);
Assert.Equal(txid, evaluation.InputContext.TxId);
Assert.Equal(address, evaluation.InputContext.Miner);
Assert.Equal(1, evaluation.InputContext.BlockIndex);
Assert.Null(
Expand All @@ -48,5 +72,126 @@ public void Constructor()
evaluation.OutputStates.GetState(address)
);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task EvaluateActionsGradually(bool rehearsal)
{
var fx = new IntegerSet(new[] { 5, 10 });

// a: ((5 + 1) * 2) + 3 = 15
(Transaction<Arithmetic> a, var deltaA) = fx.Sign(
0,
Arithmetic.Add(1),
Arithmetic.Mul(2),
Arithmetic.Add(3)
);

Block<Arithmetic> blockA = await fx.Mine();
ActionEvaluation[] evalsA = ActionEvaluation.EvaluateActionsGradually(
blockA.Hash,
blockIndex: blockA.Index,
txid: a.Id,
previousStates: fx.CreateAccountStateDelta(0, blockA.PreviousHash),
minerAddress: blockA.Miner ?? default,
signer: a.Signer,
signature: a.Signature,
actions: a.Actions.ToImmutableArray<IAction>(),
rehearsal: rehearsal,
previousBlockStatesTrie: fx.GetTrie(blockA.PreviousHash),
blockAction: false
).ToArray();

Assert.Equal(evalsA.Length, deltaA.Count - 1);
for (int i = 0; i < evalsA.Length; i++)
{
ActionEvaluation eval = evalsA[i];
_logger.Debug("evalsA[{0}] = {1}", i, eval);
_logger.Debug("a.Actions[{0}] = {1}", i, a.Actions[i]);
Assert.Equal(a.Actions[i], eval.Action);
IActionContext context = eval.InputContext;
Assert.Equal(blockA.Miner, context.Miner);
Assert.Equal(blockA.Index, context.BlockIndex);
Assert.Equal(
deltaA[i].RootHash,
context.PreviousStateRootHash
);
Assert.Equal(a.Signer, context.Signer);
Assert.False(context.BlockAction);
Assert.Equal(rehearsal, context.Rehearsal);
IAccountStateDelta prevStates = context.PreviousStates;
Assert.Equal(
i > 0 ? new[] { a.Signer } : new Address[0],
prevStates.UpdatedAddresses
);
Assert.Equal((Integer)deltaA[i].Value, prevStates.GetState(a.Signer));
IAccountStateDelta outputStates = eval.OutputStates;
Assert.Equal(new[] { a.Signer }, outputStates.UpdatedAddresses);
Assert.Equal((Integer)deltaA[i + 1].Value, outputStates.GetState(a.Signer));
Assert.Null(eval.Exception);
}

// b: error(10 - 3) + -3 =
// (10 - 3) = 7 (only input of error() is left)
(Transaction<Arithmetic> b, var deltaB) = fx.Sign(
1,
Arithmetic.Sub(3),
new Arithmetic(),
Arithmetic.Add(-1)
);

Block<Arithmetic> blockB = await fx.Mine();
ActionEvaluation[] evalsB = ActionEvaluation.EvaluateActionsGradually(
blockB.Hash,
blockIndex: blockB.Index,
txid: b.Id,
previousStates: fx.CreateAccountStateDelta(0, blockB.PreviousHash),
minerAddress: blockB.Miner ?? default,
signer: b.Signer,
signature: b.Signature,
actions: b.Actions.ToImmutableArray<IAction>(),
rehearsal: rehearsal,
previousBlockStatesTrie: fx.GetTrie(blockB.PreviousHash),
blockAction: false
).ToArray();

Assert.Equal(evalsB.Length, deltaB.Count - 1);
for (int i = 0; i < evalsB.Length; i++)
{
ActionEvaluation eval = evalsB[i];
_logger.Debug("evalsB[{0}] = {@1}", i, eval);
_logger.Debug("b.Actions[{0}] = {@1}", i, b.Actions[i]);
Assert.Equal(b.Actions[i], eval.Action);
IActionContext context = eval.InputContext;
Assert.Equal(blockB.Miner, context.Miner);
Assert.Equal(blockB.Index, context.BlockIndex);
Assert.Equal(
deltaB[i].RootHash,
context.PreviousStateRootHash
);
Assert.Equal(b.Signer, context.Signer);
Assert.False(context.BlockAction);
Assert.Equal(rehearsal, context.Rehearsal);
IAccountStateDelta prevStates = context.PreviousStates;
Assert.Equal(
i > 0 ? new[] { b.Signer } : new Address[0],
prevStates.UpdatedAddresses
);
Assert.Equal((Integer)deltaB[i].Value, prevStates.GetState(b.Signer));
IAccountStateDelta outputStates = eval.OutputStates;
Assert.Equal(new[] { b.Signer }, outputStates.UpdatedAddresses);
Assert.Equal((Integer)deltaB[i + 1].Value, outputStates.GetState(b.Signer));
if (i == 1)
{
Assert.IsType<UnexpectedlyTerminatedActionException>(eval.Exception);
Assert.IsType<InvalidOperationException>(eval.Exception.InnerException);
}
else
{
Assert.Null(eval.Exception);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class AnonymousActionRendererTest
new AccountStateDeltaImpl(_ => null, (_, __) => default, default);

private static IActionContext _actionContext =
new ActionContext(default, default, default, _stateDelta, default);
new ActionContext(default, default, default, default, _stateDelta, default);

private static Exception _exception = new Exception();

Expand Down
Loading

0 comments on commit 1474b66

Please sign in to comment.