From 37100b8697342bd50feda951b91f357fb95f214f Mon Sep 17 00:00:00 2001 From: minhoryang Date: Sat, 17 Aug 2019 02:31:35 +0900 Subject: [PATCH] Resolve #172, Less stateful RawTransaction --- CHANGES.md | 9 +++ Libplanet.Tests/Blocks/BlockTest.cs | 28 ++++----- Libplanet.Tests/Tx/TransactionTest.cs | 10 +-- Libplanet/Tx/RawTransaction.cs | 87 ++++++++++++++++----------- Libplanet/Tx/Transaction.cs | 14 ++--- 5 files changed, 87 insertions(+), 61 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2688e0b5fc..38658a1945 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,13 @@ To be released. - Added `IActionContext.NewGuId()` method. [[#371], [#439]] - `Address(byte[])` became to throw `ArgumentNullException` instead of `NullReferenceException`. [[#443]] + - `RawTransaction.Signer` became to `ImmutableArray` type. [[#172], [#441]] + - `RawTransaction.PublicKey` became to `ImmutableArray` type. [[#172], [#441]] + - `RawTransaction.Signature` became to `ImmutableArray` type. [[#172], [#441]] + - `RawTransaction.UpdatedAddresses` became to + `ImmutableArray>` type. [[#172], [#441]] + - `RawTransaction.Actions` became to + `IEnumerable>` type. [[#172], [#441]] ### Added interfaces @@ -114,6 +121,7 @@ To be released. - Fixed a bug that TURN relay connection had disconnected when preloading took a long time. [[#424]] +[#172]: https://github.com/planetarium/libplanet/issues/172 [#319]: https://github.com/planetarium/libplanet/issues/319 [#343]: https://github.com/planetarium/libplanet/pull/343 [#350]: https://github.com/planetarium/libplanet/pull/350 @@ -152,6 +160,7 @@ To be released. [#434]: https://github.com/planetarium/libplanet/pull/434 [#438]: https://github.com/planetarium/libplanet/pull/438 [#439]: https://github.com/planetarium/libplanet/pull/439 +[#441]: https://github.com/planetarium/libplanet/pull/441 [#442]: https://github.com/planetarium/libplanet/issues/442 [#443]: https://github.com/planetarium/libplanet/pull/443 [LiteDB #1268]: https://github.com/mbdavid/LiteDB/issues/1268 diff --git a/Libplanet.Tests/Blocks/BlockTest.cs b/Libplanet.Tests/Blocks/BlockTest.cs index 464ffc5f4c..79bf6a43bf 100644 --- a/Libplanet.Tests/Blocks/BlockTest.cs +++ b/Libplanet.Tests/Blocks/BlockTest.cs @@ -339,11 +339,11 @@ public void EvaluateInvalidTxSignature() RawTransaction rawTx = new RawTransaction( 0, _fx.TxFixture.Address1.ByteArray, - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + ImmutableArray>.Empty, + _fx.TxFixture.PublicKey1.Format(false).ToImmutableArray(), DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"), - new IDictionary[0], - new byte[10] + new IImmutableDictionary[0], + new byte[10].ToImmutableArray() ); var invalidTx = new Transaction(rawTx); Block invalidBlock = MineNext( @@ -361,11 +361,11 @@ public void EvaluateInvalidTxPublicKey() RawTransaction rawTxWithoutSig = new RawTransaction( 0, new PrivateKey().PublicKey.ToAddress().ByteArray, - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + ImmutableArray>.Empty, + _fx.TxFixture.PublicKey1.Format(false).ToImmutableArray(), DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"), - new IDictionary[0], - new byte[0] + new IImmutableDictionary[0], + ImmutableArray.Empty ); byte[] sig = _fx.TxFixture.PrivateKey1.Sign( new Transaction(rawTxWithoutSig).ToBencodex(false) @@ -378,7 +378,7 @@ public void EvaluateInvalidTxPublicKey() rawTxWithoutSig.PublicKey, rawTxWithoutSig.Timestamp, rawTxWithoutSig.Actions, - sig + sig.ToImmutableArray() ) ); Block invalidBlock = MineNext( @@ -393,17 +393,17 @@ public void EvaluateInvalidTxPublicKey() [Fact] public void EvaluateInvalidTxUpdatedAddresses() { - ImmutableArray> rawActions = + ImmutableArray> rawActions = _fx.TxFixture.TxWithActions .ToRawTransaction(false).Actions.ToImmutableArray(); RawTransaction rawTxWithoutSig = new RawTransaction( 0, _fx.TxFixture.Address1.ByteArray, - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + ImmutableArray>.Empty, + _fx.TxFixture.PublicKey1.Format(false).ToImmutableArray(), DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"), rawActions, - new byte[0] + ImmutableArray.Empty ); byte[] sig = _fx.TxFixture.PrivateKey1.Sign( new Transaction>( @@ -418,7 +418,7 @@ public void EvaluateInvalidTxUpdatedAddresses() rawTxWithoutSig.PublicKey, rawTxWithoutSig.Timestamp, rawTxWithoutSig.Actions, - sig + sig.ToImmutableArray() ) ); Block> invalidBlock = MineNext( diff --git a/Libplanet.Tests/Tx/TransactionTest.cs b/Libplanet.Tests/Tx/TransactionTest.cs index f83d01c3b1..0179cfd822 100644 --- a/Libplanet.Tests/Tx/TransactionTest.cs +++ b/Libplanet.Tests/Tx/TransactionTest.cs @@ -750,7 +750,7 @@ public void DetectBadSignature() rawTx.PublicKey, rawTx.Timestamp, rawTx.Actions, - new byte[rawTx.Signature.Length] + new byte[rawTx.Signature.Length].ToImmutableArray() ) ); @@ -783,7 +783,7 @@ public void DetectAddressMismatch() publicKey: rawTx.PublicKey, timestamp: rawTx.Timestamp, actions: rawTx.Actions, - signature: signature + signature: signature.ToImmutableArray() ); var tx = new Transaction(rawTxWithMismatchedAddress); @@ -896,7 +896,7 @@ internal RawTransaction GetExpectedRawTransaction(bool includeSingature) 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d, 0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb, 0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc, }.ToImmutableArray(), - updatedAddresses: new byte[][] { }, + updatedAddresses: ImmutableArray>.Empty, publicKey: new byte[] { 0x04, 0x46, 0x11, 0x5b, 0x01, 0x31, 0xba, 0xcc, 0xf9, 0x4a, @@ -906,9 +906,9 @@ internal RawTransaction GetExpectedRawTransaction(bool includeSingature) 0x1f, 0x11, 0x0a, 0x32, 0x6d, 0xa1, 0xbd, 0xb8, 0x1f, 0x5a, 0xe3, 0xba, 0xdf, 0x76, 0xa9, 0x0b, 0x22, 0xc8, 0xc4, 0x91, 0xae, 0xd3, 0xaa, 0xa2, 0x96, - }, + }.ToImmutableArray(), timestamp: "2018-11-21T00:00:00.000000Z", - actions: new List>() + actions: new List>() ); if (!includeSingature) { diff --git a/Libplanet/Tx/RawTransaction.cs b/Libplanet/Tx/RawTransaction.cs index 1fd4aa1d63..93f1f6428b 100644 --- a/Libplanet/Tx/RawTransaction.cs +++ b/Libplanet/Tx/RawTransaction.cs @@ -18,14 +18,19 @@ public RawTransaction(SerializationInfo info, StreamingContext context) : this( nonce: info.GetInt64("nonce"), signer: info.GetValue("signer").ToImmutableArray(), - publicKey: info.GetValue("public_key"), + publicKey: info.GetValue("public_key").ToImmutableArray(), updatedAddresses: To2dArray( info.GetValue("updated_addresses"), Address.Size), timestamp: info.GetString("timestamp"), - signature: info.GetValue("signature"), + signature: info.GetValue("signature").ToImmutableArray(), actions: info.GetValue( - "actions").OfType>() + "actions").OfType>().Select(a => + a.ToImmutableDictionary( + kv => kv.Key, + kv => kv.Value + ) + ) ) { } @@ -33,10 +38,10 @@ public RawTransaction(SerializationInfo info, StreamingContext context) public RawTransaction( long nonce, ImmutableArray signer, - byte[][] updatedAddresses, - byte[] publicKey, + ImmutableArray> updatedAddresses, + ImmutableArray publicKey, string timestamp, - IEnumerable> actions + IEnumerable> actions ) : this( nonce, @@ -45,7 +50,7 @@ IEnumerable> actions publicKey, timestamp, actions, - null + ImmutableArray.Empty ) { } @@ -53,11 +58,11 @@ IEnumerable> actions public RawTransaction( long nonce, ImmutableArray signer, - byte[][] updatedAddresses, - byte[] publicKey, + ImmutableArray> updatedAddresses, + ImmutableArray publicKey, string timestamp, - IEnumerable> actions, - byte[] signature + IEnumerable> actions, + ImmutableArray signature ) { Nonce = nonce; @@ -76,18 +81,24 @@ public RawTransaction(Dictionary dict) UpdatedAddresses = To2dArray( (byte[])dict["updated_addresses"], Address.Size); - PublicKey = (byte[])dict["public_key"]; + PublicKey = ((byte[])dict["public_key"]).ToImmutableArray(); Timestamp = (string)dict["timestamp"]; Actions = ((IEnumerable)dict["actions"]) .Cast>() + .Select(a => + a.ToImmutableDictionary( + kv => kv.Key, + kv => kv.Value + ) + ) .ToList(); if (dict.TryGetValue("signature", out object signature)) { - Signature = (byte[])signature; + Signature = ((byte[])signature).ToImmutableArray(); } else { - Signature = null; + Signature = ImmutableArray.Empty; } } @@ -95,15 +106,15 @@ public RawTransaction(Dictionary dict) public ImmutableArray Signer { get; } - public byte[] PublicKey { get; } + public ImmutableArray PublicKey { get; } - public byte[][] UpdatedAddresses { get; } + public ImmutableArray> UpdatedAddresses { get; } public string Timestamp { get; } - public byte[] Signature { get; } + public ImmutableArray Signature { get; } - public IEnumerable> Actions { get; } + public IEnumerable> Actions { get; } public void GetObjectData( SerializationInfo info, @@ -111,7 +122,7 @@ StreamingContext context ) { info.AddValue("nonce", Nonce); - info.AddValue("signer", Signer); + info.AddValue("signer", Signer.ToArray()); // SerializationInfo.AddValue() doesn't seem to work well with // 2d arrays. Concat addresses before encode them (fortunately, @@ -119,7 +130,7 @@ StreamingContext context var updatedAddresses = new byte[UpdatedAddresses.Length * Address.Size]; int i = 0; - foreach (byte[] address in UpdatedAddresses) + foreach (ImmutableArray address in UpdatedAddresses) { address.CopyTo(updatedAddresses, i); i += Address.Size; @@ -127,13 +138,18 @@ StreamingContext context info.AddValue("updated_addresses", updatedAddresses); - info.AddValue("public_key", PublicKey); + info.AddValue("public_key", PublicKey.ToArray()); info.AddValue("timestamp", Timestamp); - info.AddValue("actions", Actions); - - if (Signature != null) + info.AddValue("actions", Actions.Select(a => + a.ToDictionary( + kv => kv.Key, + kv => kv.Value + ) + )); + + if (Signature != ImmutableArray.Empty) { - info.AddValue("signature", Signature); + info.AddValue("signature", Signature.ToArray()); } } @@ -146,31 +162,31 @@ public RawTransaction AddSignature(byte[] signature) PublicKey, Timestamp, Actions, - signature + signature.ToImmutableArray() // FIXME: New API? ); } public override int GetHashCode() { - return ByteUtil.CalculateHashCode(Signature); + return ByteUtil.CalculateHashCode(Signature.ToArray()); } public override string ToString() { string updatedAddresses = string.Join( string.Empty, - UpdatedAddresses.Select(a => "\n " + ByteUtil.Hex(a)) + UpdatedAddresses.Select(a => "\n " + ByteUtil.Hex(a.ToArray())) ); return $@"{nameof(RawTransaction)} {nameof(Nonce)} = {Nonce.ToString()} {nameof(Signer)} = {ByteUtil.Hex(Signer.ToArray())} - {nameof(PublicKey)} = {ByteUtil.Hex(PublicKey)} + {nameof(PublicKey)} = {ByteUtil.Hex(PublicKey.ToArray())} {nameof(UpdatedAddresses)} = {updatedAddresses} {nameof(Timestamp)} = {Timestamp} - {nameof(Signature)} = {ByteUtil.Hex(Signature)}"; + {nameof(Signature)} = {ByteUtil.Hex(Signature.ToArray())}"; } - private static T[][] To2dArray(T[] array, int chunk) + private static ImmutableArray> To2dArray(T[] array, int chunk) { if (array.Length % chunk > 0) { @@ -181,14 +197,15 @@ private static T[][] To2dArray(T[] array, int chunk) } int resultLength = array.Length / chunk; - T[][] result = new T[resultLength][]; + ImmutableArray[] result = new ImmutableArray[resultLength]; for (int i = 0; i < resultLength; i++) { - result[i] = new T[chunk]; - Array.Copy(array, i * chunk, result[i], 0, chunk); + T[] partialResult = new T[chunk]; + Array.Copy(array, i * chunk, partialResult, 0, chunk); + result[i] = partialResult.ToImmutableArray(); } - return result; + return result.ToImmutableArray(); } } } diff --git a/Libplanet/Tx/Transaction.cs b/Libplanet/Tx/Transaction.cs index 87b717b293..85f575ff08 100644 --- a/Libplanet/Tx/Transaction.cs +++ b/Libplanet/Tx/Transaction.cs @@ -106,8 +106,8 @@ public Transaction( internal Transaction(RawTransaction rawTx) : this( rawTx.Nonce, - new Address(rawTx.Signer.ToArray()), // FIXME(minhoryang): Propagate? - new PublicKey(rawTx.PublicKey), + new Address(rawTx.Signer), + new PublicKey(rawTx.PublicKey.ToArray()), // FIXME: propagate? rawTx.UpdatedAddresses.Select( a => new Address(a) ).ToImmutableHashSet(), @@ -116,7 +116,7 @@ internal Transaction(RawTransaction rawTx) TimestampFormat, CultureInfo.InvariantCulture).ToUniversalTime(), rawTx.Actions.Select(ToAction).ToImmutableList(), - rawTx.Signature, + rawTx.Signature.ToArray(), // FIXME: propagate? false) { } @@ -665,11 +665,11 @@ internal RawTransaction ToRawTransaction(bool includeSign) nonce: Nonce, signer: Signer.ByteArray, updatedAddresses: UpdatedAddresses.Select(a => - a.ToByteArray()).ToArray(), - publicKey: PublicKey.Format(false), + a.ByteArray).ToImmutableArray(), + publicKey: PublicKey.Format(false).ToImmutableArray(), timestamp: Timestamp.ToString(TimestampFormat), actions: Actions.Select(a => - a.PlainValue.ToDictionary( + a.PlainValue.ToImmutableDictionary( kv => kv.Key, kv => kv.Value ) @@ -684,7 +684,7 @@ internal RawTransaction ToRawTransaction(bool includeSign) return rawTx; } - private static T ToAction(IDictionary arg) + private static T ToAction(IImmutableDictionary arg) { var action = new T(); action.LoadPlainValue(arg.ToImmutableDictionary());