diff --git a/Libplanet.Tests/Blocks/BlockTest.cs b/Libplanet.Tests/Blocks/BlockTest.cs index 3e877ee361..79bf6a43bf 100644 --- a/Libplanet.Tests/Blocks/BlockTest.cs +++ b/Libplanet.Tests/Blocks/BlockTest.cs @@ -338,12 +338,12 @@ public void EvaluateInvalidTxSignature() { RawTransaction rawTx = new RawTransaction( 0, - _fx.TxFixture.Address1.ToByteArray(), - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + _fx.TxFixture.Address1.ByteArray, + 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( @@ -360,12 +360,12 @@ public void EvaluateInvalidTxPublicKey() { RawTransaction rawTxWithoutSig = new RawTransaction( 0, - new PrivateKey().PublicKey.ToAddress().ToByteArray(), - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + new PrivateKey().PublicKey.ToAddress().ByteArray, + 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.ToByteArray(), - new byte[][] { }, - _fx.TxFixture.PublicKey1.Format(false), + _fx.TxFixture.Address1.ByteArray, + 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 8bd0a589dc..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() ) ); @@ -778,12 +778,12 @@ public void DetectAddressMismatch() }; var rawTxWithMismatchedAddress = new RawTransaction( nonce: 0, - signer: mismatchedAddress, + signer: mismatchedAddress.ToImmutableArray(), updatedAddresses: rawTx.UpdatedAddresses, publicKey: rawTx.PublicKey, timestamp: rawTx.Timestamp, actions: rawTx.Actions, - signature: signature + signature: signature.ToImmutableArray() ); var tx = new Transaction(rawTxWithMismatchedAddress); @@ -895,8 +895,8 @@ 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, - }, - updatedAddresses: new byte[][] { }, + }.ToImmutableArray(), + 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 ddf8b30f5f..93f1f6428b 100644 --- a/Libplanet/Tx/RawTransaction.cs +++ b/Libplanet/Tx/RawTransaction.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -16,26 +17,31 @@ internal struct RawTransaction : ISerializable public RawTransaction(SerializationInfo info, StreamingContext context) : this( nonce: info.GetInt64("nonce"), - signer: info.GetValue("signer"), - publicKey: info.GetValue("public_key"), + signer: info.GetValue("signer").ToImmutableArray(), + 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 + ) + ) ) { } public RawTransaction( long nonce, - byte[] signer, - byte[][] updatedAddresses, - byte[] publicKey, + ImmutableArray signer, + ImmutableArray> updatedAddresses, + ImmutableArray publicKey, string timestamp, - IEnumerable> actions + IEnumerable> actions ) : this( nonce, @@ -44,19 +50,19 @@ IEnumerable> actions publicKey, timestamp, actions, - null + ImmutableArray.Empty ) { } public RawTransaction( long nonce, - byte[] signer, - byte[][] updatedAddresses, - byte[] publicKey, + ImmutableArray signer, + ImmutableArray> updatedAddresses, + ImmutableArray publicKey, string timestamp, - IEnumerable> actions, - byte[] signature + IEnumerable> actions, + ImmutableArray signature ) { Nonce = nonce; @@ -71,38 +77,44 @@ byte[] signature public RawTransaction(Dictionary dict) { Nonce = (long)(BigInteger)dict["nonce"]; - Signer = (byte[])dict["signer"]; + Signer = ((byte[])dict["signer"]).ToImmutableArray(); 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; } } public long Nonce { get; } - public byte[] Signer { get; } + 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, @@ -110,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, @@ -118,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; @@ -126,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()); } } @@ -145,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)} - {nameof(PublicKey)} = {ByteUtil.Hex(PublicKey)} + {nameof(Signer)} = {ByteUtil.Hex(Signer.ToArray())} + {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) { @@ -180,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 2218d83918..85f575ff08 100644 --- a/Libplanet/Tx/Transaction.cs +++ b/Libplanet/Tx/Transaction.cs @@ -107,7 +107,7 @@ internal Transaction(RawTransaction rawTx) : this( rawTx.Nonce, new Address(rawTx.Signer), - new PublicKey(rawTx.PublicKey), + 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) { } @@ -663,13 +663,13 @@ internal RawTransaction ToRawTransaction(bool includeSign) { var rawTx = new RawTransaction( nonce: Nonce, - signer: Signer.ToByteArray(), + 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());