diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs index 114ceffc8d..d0f63e5c1c 100644 --- a/neo.UnitTests/IO/UT_IOHelper.cs +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -23,6 +23,46 @@ public void TestAsSerializableGeneric() Assert.AreEqual(UInt160.Zero, result); } + [TestMethod] + public void TestNullableArray() + { + var caseArray = new UInt160[] + { + null, UInt160.Zero, new UInt160( + new byte[] { + 0xAA,0x00,0x00,0x00,0x00, + 0xBB,0x00,0x00,0x00,0x00, + 0xCC,0x00,0x00,0x00,0x00, + 0xDD,0x00,0x00,0x00,0x00 + }) + }; + + byte[] data; + using (var stream = new MemoryStream()) + using (var writter = new BinaryWriter(stream)) + { + Neo.IO.Helper.WriteNullableArray(writter, caseArray); + data = stream.ToArray(); + } + + // Read Error + + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + Assert.ThrowsException(() => Neo.IO.Helper.ReadNullableArray(reader, 2)); + } + + // Read 100% + + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + var read = Neo.IO.Helper.ReadNullableArray(reader); + CollectionAssert.AreEqual(caseArray, read); + } + } + [TestMethod] public void TestAsSerializable() { @@ -48,7 +88,7 @@ public void TestAsSerializable() [TestMethod] public void TestAsSerializableArray() { - byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); + byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); UInt160[] result = Neo.IO.Helper.AsSerializableArray(byteArray); Assert.AreEqual(1, result.Length); Assert.AreEqual(UInt160.Zero, result[0]); diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 0d9edbb3dc..ddbf6caeea 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -104,18 +104,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < PreparationPayloads.Length; i++) - PreparationPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - CommitPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < CommitPayloads.Length; i++) - CommitPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - ChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < ChangeViewPayloads.Length; i++) - ChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - LastChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < LastChangeViewPayloads.Length; i++) - LastChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + PreparationPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + CommitPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + ChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); @@ -416,38 +408,10 @@ public void Serialize(BinaryWriter writer) writer.Write(ViewNumber); writer.Write(TransactionHashes ?? new UInt256[0]); writer.Write(Transactions?.Values.ToArray() ?? new Transaction[0]); - writer.WriteVarInt(PreparationPayloads.Length); - foreach (var payload in PreparationPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(CommitPayloads.Length); - foreach (var payload in CommitPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(ChangeViewPayloads.Length); - foreach (var payload in ChangeViewPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(LastChangeViewPayloads.Length); - foreach (var payload in LastChangeViewPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } + writer.WriteNullableArray(PreparationPayloads); + writer.WriteNullableArray(CommitPayloads); + writer.WriteNullableArray(ChangeViewPayloads); + writer.WriteNullableArray(LastChangeViewPayloads); } } } diff --git a/neo/IO/Helper.cs b/neo/IO/Helper.cs index c985e676b2..c47878579d 100644 --- a/neo/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -112,6 +112,14 @@ public static string ReadFixedString(this BinaryReader reader, int length) return Encoding.UTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); } + public static T[] ReadNullableArray(this BinaryReader reader, int max = 0x1000000) where T : class, ISerializable, new() + { + T[] array = new T[reader.ReadVarInt((ulong)max)]; + for (int i = 0; i < array.Length; i++) + array[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + return array; + } + public static T ReadSerializable(this BinaryReader reader) where T : ISerializable, new() { T obj = new T(); @@ -225,6 +233,18 @@ public static void WriteFixedString(this BinaryWriter writer, string value, int writer.Write(new byte[length - bytes.Length]); } + public static void WriteNullableArray(this BinaryWriter writer, T[] value) where T : class, ISerializable + { + writer.WriteVarInt(value.Length); + foreach (var item in value) + { + bool isNull = item is null; + writer.Write(!isNull); + if (isNull) continue; + item.Serialize(writer); + } + } + public static void WriteVarBytes(this BinaryWriter writer, byte[] value) { writer.WriteVarInt(value.Length);