From 36025a2f8eaa0f2c6dfc7cd0e01d551667d23219 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Nov 2019 17:23:28 +0800 Subject: [PATCH 1/5] Improve p2p message deserialization --- neo/Network/P2P/Message.cs | 52 ++++------------------------- neo/Network/P2P/MessageCommand.cs | 18 ++++++++++ neo/Network/P2P/PayloadAttribute.cs | 15 +++++++++ 3 files changed, 39 insertions(+), 46 deletions(-) create mode 100644 neo/Network/P2P/PayloadAttribute.cs diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 38bc2bf6c1..6204604e11 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -1,9 +1,9 @@ using Akka.IO; using Neo.Cryptography; using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; +using System.Reflection; namespace Neo.Network.P2P { @@ -51,51 +51,11 @@ private void DecompressPayload() byte[] decompressed = Flags.HasFlag(MessageFlags.Compressed) ? _payload_compressed.DecompressLz4(PayloadMaxSize) : _payload_compressed; - switch (Command) - { - case MessageCommand.Version: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Addr: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Ping: - case MessageCommand.Pong: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.GetHeaders: - case MessageCommand.GetBlocks: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Headers: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Inv: - case MessageCommand.GetData: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.GetBlockData: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Transaction: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Block: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Consensus: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.FilterLoad: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.FilterAdd: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.MerkleBlock: - Payload = decompressed.AsSerializable(); - break; - } + MemberInfo[] members = typeof(MessageCommand).GetMember(Command.ToString()); + if (members?.Length != 1) return; + PayloadAttribute attribute = members[0].GetCustomAttribute(); + if (attribute is null) return; + Payload = decompressed.AsSerializable(attribute.Type); } void ISerializable.Deserialize(BinaryReader reader) diff --git a/neo/Network/P2P/MessageCommand.cs b/neo/Network/P2P/MessageCommand.cs index d1f2befc67..e6727eba61 100644 --- a/neo/Network/P2P/MessageCommand.cs +++ b/neo/Network/P2P/MessageCommand.cs @@ -1,35 +1,53 @@ +using Neo.Network.P2P.Payloads; + namespace Neo.Network.P2P { public enum MessageCommand : byte { //handshaking + [Payload(typeof(VersionPayload))] Version = 0x00, Verack = 0x01, //connectivity GetAddr = 0x10, + [Payload(typeof(AddrPayload))] Addr = 0x11, + [Payload(typeof(PingPayload))] Ping = 0x18, + [Payload(typeof(PingPayload))] Pong = 0x19, //synchronization + [Payload(typeof(GetBlocksPayload))] GetHeaders = 0x20, + [Payload(typeof(HeadersPayload))] Headers = 0x21, + [Payload(typeof(GetBlocksPayload))] GetBlocks = 0x24, Mempool = 0x25, + [Payload(typeof(InvPayload))] Inv = 0x27, + [Payload(typeof(InvPayload))] GetData = 0x28, + [Payload(typeof(GetBlockDataPayload))] GetBlockData = 0x29, NotFound = 0x2a, + [Payload(typeof(Transaction))] Transaction = 0x2b, + [Payload(typeof(Block))] Block = 0x2c, + [Payload(typeof(ConsensusPayload))] Consensus = 0x2d, Reject = 0x2f, //SPV protocol + [Payload(typeof(FilterLoadPayload))] FilterLoad = 0x30, + [Payload(typeof(FilterAddPayload))] FilterAdd = 0x31, FilterClear = 0x32, + [Payload(typeof(MerkleBlockPayload))] MerkleBlock = 0x38, //others diff --git a/neo/Network/P2P/PayloadAttribute.cs b/neo/Network/P2P/PayloadAttribute.cs new file mode 100644 index 0000000000..3db3e77c60 --- /dev/null +++ b/neo/Network/P2P/PayloadAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Neo.Network.P2P +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + internal class PayloadAttribute : Attribute + { + public Type Type { get; } + + public PayloadAttribute(Type type) + { + Type = type; + } + } +} From 5b2a046093cac5b321847a7429daf1d4dc74afd4 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Nov 2019 17:31:33 +0800 Subject: [PATCH 2/5] Reuse ReflectionCacheAttribute --- neo/IO/Caching/ReflectionCacheAttribute.cs | 5 ++-- neo/Network/P2P/Message.cs | 3 +- neo/Network/P2P/MessageCommand.cs | 33 +++++++++++----------- neo/Network/P2P/PayloadAttribute.cs | 15 ---------- 4 files changed, 22 insertions(+), 34 deletions(-) delete mode 100644 neo/Network/P2P/PayloadAttribute.cs diff --git a/neo/IO/Caching/ReflectionCacheAttribute.cs b/neo/IO/Caching/ReflectionCacheAttribute.cs index b2ac3c3054..2bbbca8568 100644 --- a/neo/IO/Caching/ReflectionCacheAttribute.cs +++ b/neo/IO/Caching/ReflectionCacheAttribute.cs @@ -2,12 +2,13 @@ namespace Neo.IO.Caching { - public class ReflectionCacheAttribute : Attribute + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + internal class ReflectionCacheAttribute : Attribute { /// /// Type /// - public Type Type { get; private set; } + public Type Type { get; } /// /// Constructor diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 6204604e11..33acaa80e6 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -1,6 +1,7 @@ using Akka.IO; using Neo.Cryptography; using Neo.IO; +using Neo.IO.Caching; using System; using System.IO; using System.Reflection; @@ -53,7 +54,7 @@ private void DecompressPayload() : _payload_compressed; MemberInfo[] members = typeof(MessageCommand).GetMember(Command.ToString()); if (members?.Length != 1) return; - PayloadAttribute attribute = members[0].GetCustomAttribute(); + ReflectionCacheAttribute attribute = members[0].GetCustomAttribute(); if (attribute is null) return; Payload = decompressed.AsSerializable(attribute.Type); } diff --git a/neo/Network/P2P/MessageCommand.cs b/neo/Network/P2P/MessageCommand.cs index e6727eba61..ccddacfba2 100644 --- a/neo/Network/P2P/MessageCommand.cs +++ b/neo/Network/P2P/MessageCommand.cs @@ -1,3 +1,4 @@ +using Neo.IO.Caching; using Neo.Network.P2P.Payloads; namespace Neo.Network.P2P @@ -5,49 +6,49 @@ namespace Neo.Network.P2P public enum MessageCommand : byte { //handshaking - [Payload(typeof(VersionPayload))] + [ReflectionCache(typeof(VersionPayload))] Version = 0x00, Verack = 0x01, //connectivity GetAddr = 0x10, - [Payload(typeof(AddrPayload))] + [ReflectionCache(typeof(AddrPayload))] Addr = 0x11, - [Payload(typeof(PingPayload))] + [ReflectionCache(typeof(PingPayload))] Ping = 0x18, - [Payload(typeof(PingPayload))] + [ReflectionCache(typeof(PingPayload))] Pong = 0x19, //synchronization - [Payload(typeof(GetBlocksPayload))] + [ReflectionCache(typeof(GetBlocksPayload))] GetHeaders = 0x20, - [Payload(typeof(HeadersPayload))] + [ReflectionCache(typeof(HeadersPayload))] Headers = 0x21, - [Payload(typeof(GetBlocksPayload))] + [ReflectionCache(typeof(GetBlocksPayload))] GetBlocks = 0x24, Mempool = 0x25, - [Payload(typeof(InvPayload))] + [ReflectionCache(typeof(InvPayload))] Inv = 0x27, - [Payload(typeof(InvPayload))] + [ReflectionCache(typeof(InvPayload))] GetData = 0x28, - [Payload(typeof(GetBlockDataPayload))] + [ReflectionCache(typeof(GetBlockDataPayload))] GetBlockData = 0x29, NotFound = 0x2a, - [Payload(typeof(Transaction))] + [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, - [Payload(typeof(Block))] + [ReflectionCache(typeof(Block))] Block = 0x2c, - [Payload(typeof(ConsensusPayload))] + [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, Reject = 0x2f, //SPV protocol - [Payload(typeof(FilterLoadPayload))] + [ReflectionCache(typeof(FilterLoadPayload))] FilterLoad = 0x30, - [Payload(typeof(FilterAddPayload))] + [ReflectionCache(typeof(FilterAddPayload))] FilterAdd = 0x31, FilterClear = 0x32, - [Payload(typeof(MerkleBlockPayload))] + [ReflectionCache(typeof(MerkleBlockPayload))] MerkleBlock = 0x38, //others diff --git a/neo/Network/P2P/PayloadAttribute.cs b/neo/Network/P2P/PayloadAttribute.cs deleted file mode 100644 index 3db3e77c60..0000000000 --- a/neo/Network/P2P/PayloadAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Neo.Network.P2P -{ - [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] - internal class PayloadAttribute : Attribute - { - public Type Type { get; } - - public PayloadAttribute(Type type) - { - Type = type; - } - } -} From 5fb7d8796f212ad5b2dc86fc849a0629e036a3a1 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Nov 2019 18:09:13 +0800 Subject: [PATCH 3/5] Improve ReflectionCache --- .../IO/Caching/UT_ReflectionCache.cs | 35 ++-------- neo/Consensus/ConsensusMessage.cs | 7 +- neo/IO/Caching/ReflectionCache.cs | 69 ++++--------------- 3 files changed, 20 insertions(+), 91 deletions(-) diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index 4e1fcce634..8f7b51fadc 100644 --- a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; -using System; namespace Neo.UnitTests.IO.Caching { @@ -25,44 +24,22 @@ public enum MyEmptyEnum : byte { } [TestClass] public class UT_ReflectionCache { - ReflectionCache reflectionCache; - - [TestInitialize] - public void SetUp() - { - reflectionCache = ReflectionCache.CreateFromEnum(); - } - - [TestMethod] - public void TestCreateFromEnum() - { - reflectionCache.Should().NotBeNull(); - } - - [TestMethod] - public void TestCreateFromObjectNotEnum() - { - Action action = () => ReflectionCache.CreateFromEnum(); - action.Should().Throw(); - } - [TestMethod] public void TestCreateFromEmptyEnum() { - reflectionCache = ReflectionCache.CreateFromEnum(); - reflectionCache.Count.Should().Be(0); + ReflectionCache.Count.Should().Be(0); } [TestMethod] public void TestCreateInstance() { - object item1 = reflectionCache.CreateInstance((byte)MyTestEnum.Item1, null); + object item1 = ReflectionCache.CreateInstance(MyTestEnum.Item1, null); (item1 is TestItem1).Should().BeTrue(); - object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, null); + object item2 = ReflectionCache.CreateInstance(MyTestEnum.Item2, null); (item2 is TestItem2).Should().BeTrue(); - object item3 = reflectionCache.CreateInstance(0x02, null); + object item3 = ReflectionCache.CreateInstance((MyTestEnum)0x02, null); item3.Should().BeNull(); } @@ -70,10 +47,10 @@ public void TestCreateInstance() public void TestCreateInstance2() { TestItem defaultItem = new TestItem1(); - object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, defaultItem); + object item2 = ReflectionCache.CreateInstance(MyTestEnum.Item2, defaultItem); (item2 is TestItem2).Should().BeTrue(); - object item1 = reflectionCache.CreateInstance(0x02, new TestItem1()); + object item1 = ReflectionCache.CreateInstance((MyTestEnum)0x02, new TestItem1()); (item1 is TestItem1).Should().BeTrue(); } } diff --git a/neo/Consensus/ConsensusMessage.cs b/neo/Consensus/ConsensusMessage.cs index af16db03e3..385e27aeb5 100644 --- a/neo/Consensus/ConsensusMessage.cs +++ b/neo/Consensus/ConsensusMessage.cs @@ -7,11 +7,6 @@ namespace Neo.Consensus { public abstract class ConsensusMessage : ISerializable { - /// - /// Reflection cache for ConsensusMessageType - /// - private static ReflectionCache ReflectionCache = ReflectionCache.CreateFromEnum(); - public readonly ConsensusMessageType Type; public byte ViewNumber; @@ -31,7 +26,7 @@ public virtual void Deserialize(BinaryReader reader) public static ConsensusMessage DeserializeFrom(byte[] data) { - ConsensusMessage message = ReflectionCache.CreateInstance(data[0]); + ConsensusMessage message = (ConsensusMessage)ReflectionCache.CreateInstance((ConsensusMessageType)data[0]); if (message == null) throw new FormatException(); using (MemoryStream ms = new MemoryStream(data, false)) diff --git a/neo/IO/Caching/ReflectionCache.cs b/neo/IO/Caching/ReflectionCache.cs index 452856698c..56745c3c01 100644 --- a/neo/IO/Caching/ReflectionCache.cs +++ b/neo/IO/Caching/ReflectionCache.cs @@ -1,77 +1,34 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; namespace Neo.IO.Caching { - public class ReflectionCache : Dictionary + internal static class ReflectionCache where T : Enum { - /// - /// Constructor - /// - public ReflectionCache() { } - /// - /// Constructor - /// - /// Enum type - public static ReflectionCache CreateFromEnum() where EnumType : struct, IConvertible - { - Type enumType = typeof(EnumType); - - if (!enumType.GetTypeInfo().IsEnum) - throw new ArgumentException("K must be an enumerated type"); + private static readonly Dictionary dictionary = new Dictionary(); - // Cache all types - ReflectionCache r = new ReflectionCache(); + public static int Count => dictionary.Count; - foreach (object t in Enum.GetValues(enumType)) + static ReflectionCache() + { + Type enumType = typeof(T); + foreach (FieldInfo field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static)) { - // Get enumn member - MemberInfo[] memInfo = enumType.GetMember(t.ToString()); - if (memInfo == null || memInfo.Length != 1) - throw (new FormatException()); - // Get attribute - ReflectionCacheAttribute attribute = memInfo[0].GetCustomAttributes(typeof(ReflectionCacheAttribute), false) - .Cast() - .FirstOrDefault(); - - if (attribute == null) - throw (new FormatException()); + ReflectionCacheAttribute attribute = field.GetCustomAttribute(); + if (attribute == null) continue; // Append to cache - r.Add((T)t, attribute.Type); + dictionary.Add((T)field.GetValue(null), attribute.Type); } - return r; } - /// - /// Create object from key - /// - /// Key - /// Default value - public object CreateInstance(T key, object def = null) - { - Type tp; - // Get Type from cache - if (TryGetValue(key, out tp)) return Activator.CreateInstance(tp); - - // return null - return def; - } - /// - /// Create object from key - /// - /// Type - /// Key - /// Default value - public K CreateInstance(T key, K def = default(K)) + public static object CreateInstance(T key, object def = null) { - Type tp; - // Get Type from cache - if (TryGetValue(key, out tp)) return (K)Activator.CreateInstance(tp); + if (dictionary.TryGetValue(key, out Type t)) + return Activator.CreateInstance(t); // return null return def; From 206f447c068441933279065a725b52a4c41acc33 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Nov 2019 18:19:34 +0800 Subject: [PATCH 4/5] Reuse ReflectionCache --- neo/Consensus/ConsensusMessage.cs | 13 ++++--------- neo/IO/Caching/ReflectionCache.cs | 7 +++++++ neo/Network/P2P/Message.cs | 7 +------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/neo/Consensus/ConsensusMessage.cs b/neo/Consensus/ConsensusMessage.cs index 385e27aeb5..0bca5534e1 100644 --- a/neo/Consensus/ConsensusMessage.cs +++ b/neo/Consensus/ConsensusMessage.cs @@ -26,15 +26,10 @@ public virtual void Deserialize(BinaryReader reader) public static ConsensusMessage DeserializeFrom(byte[] data) { - ConsensusMessage message = (ConsensusMessage)ReflectionCache.CreateInstance((ConsensusMessageType)data[0]); - if (message == null) throw new FormatException(); - - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader r = new BinaryReader(ms)) - { - message.Deserialize(r); - } - return message; + ConsensusMessageType type = (ConsensusMessageType)data[0]; + ISerializable message = ReflectionCache.CreateSerializable(type, data); + if (message is null) throw new FormatException(); + return (ConsensusMessage)message; } public virtual void Serialize(BinaryWriter writer) diff --git a/neo/IO/Caching/ReflectionCache.cs b/neo/IO/Caching/ReflectionCache.cs index 56745c3c01..e0b92b2a92 100644 --- a/neo/IO/Caching/ReflectionCache.cs +++ b/neo/IO/Caching/ReflectionCache.cs @@ -33,5 +33,12 @@ public static object CreateInstance(T key, object def = null) // return null return def; } + + public static ISerializable CreateSerializable(T key, byte[] data) + { + if (dictionary.TryGetValue(key, out Type t)) + return data.AsSerializable(t); + return null; + } } } diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 33acaa80e6..578259f789 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -4,7 +4,6 @@ using Neo.IO.Caching; using System; using System.IO; -using System.Reflection; namespace Neo.Network.P2P { @@ -52,11 +51,7 @@ private void DecompressPayload() byte[] decompressed = Flags.HasFlag(MessageFlags.Compressed) ? _payload_compressed.DecompressLz4(PayloadMaxSize) : _payload_compressed; - MemberInfo[] members = typeof(MessageCommand).GetMember(Command.ToString()); - if (members?.Length != 1) return; - ReflectionCacheAttribute attribute = members[0].GetCustomAttribute(); - if (attribute is null) return; - Payload = decompressed.AsSerializable(attribute.Type); + Payload = ReflectionCache.CreateSerializable(Command, decompressed); } void ISerializable.Deserialize(BinaryReader reader) From 935d73d5a39c0656188b1abddea2a9d611bf4586 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 21 Nov 2019 12:47:02 +0100 Subject: [PATCH 5/5] Add UT for CreateSerializable --- .../IO/Caching/UT_ReflectionCache.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index 8f7b51fadc..547c18787d 100644 --- a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -1,10 +1,17 @@ +using System.IO; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.IO.Caching; namespace Neo.UnitTests.IO.Caching { - public class TestItem { } + public class TestItem : ISerializable + { + public int Size => 0; + public void Deserialize(BinaryReader reader) { } + public void Serialize(BinaryWriter writer) { } + } public class TestItem1 : TestItem { } @@ -43,6 +50,19 @@ public void TestCreateInstance() item3.Should().BeNull(); } + [TestMethod] + public void TestCreateSerializable() + { + object item1 = ReflectionCache.CreateSerializable(MyTestEnum.Item1, new byte[0]); + (item1 is TestItem1).Should().BeTrue(); + + object item2 = ReflectionCache.CreateSerializable(MyTestEnum.Item2, new byte[0]); + (item2 is TestItem2).Should().BeTrue(); + + object item3 = ReflectionCache.CreateSerializable((MyTestEnum)0x02, new byte[0]); + item3.Should().BeNull(); + } + [TestMethod] public void TestCreateInstance2() {