From 0f85227ded04ae2fd616bce2b2b194455d34a2fc Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 28 Dec 2020 14:45:41 +0800 Subject: [PATCH 1/2] Add MethodToken --- src/neo/SmartContract/MethodToken.cs | 42 +++++++++++++++++ src/neo/SmartContract/NefFile.cs | 45 ++++++++++++++----- .../neo.UnitTests/SmartContract/UT_Helper.cs | 2 + .../SmartContract/UT_InteropService.NEO.cs | 15 ++++--- .../neo.UnitTests/SmartContract/UT_NefFile.cs | 5 ++- 5 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/neo/SmartContract/MethodToken.cs diff --git a/src/neo/SmartContract/MethodToken.cs b/src/neo/SmartContract/MethodToken.cs new file mode 100644 index 0000000000..10396757a7 --- /dev/null +++ b/src/neo/SmartContract/MethodToken.cs @@ -0,0 +1,42 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.SmartContract +{ + public class MethodToken : ISerializable + { + public UInt160 Hash; + public string Method; + public ushort ParametersCount; + public ushort RVCount; + public CallFlags CallFlags; + + public int Size => + UInt160.Length + // Hash + Method.GetVarSize() + // Method + sizeof(ushort) + // ParametersCount + sizeof(ushort) + // RVCount + sizeof(CallFlags); // CallFlags + + void ISerializable.Deserialize(BinaryReader reader) + { + Hash = reader.ReadSerializable(); + Method = reader.ReadVarString(32); + ParametersCount = reader.ReadUInt16(); + RVCount = reader.ReadUInt16(); + CallFlags = (CallFlags)reader.ReadByte(); + if ((CallFlags & ~CallFlags.All) != 0) + throw new FormatException(); + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(Hash); + writer.WriteVarString(Method); + writer.Write(ParametersCount); + writer.Write(RVCount); + writer.Write((byte)CallFlags); + } + } +} diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index 76d3d94dc2..07c9801cbd 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -6,17 +6,22 @@ namespace Neo.SmartContract { /// - /// +------------+-----------+------------------------------------------------------------+ - /// | Field | Length | Comment | - /// +------------+-----------+------------------------------------------------------------+ - /// | Magic | 4 bytes | Magic header | - /// | Compiler | 32 bytes | Compiler used | - /// | Version | 32 bytes | Compiler version | - /// +------------+-----------+------------------------------------------------------------+ - /// | Script | Var bytes | Var bytes for the payload | - /// +------------+-----------+------------------------------------------------------------+ - /// | Checksum | 4 bytes | First four bytes of double SHA256 hash | - /// +------------+-----------+------------------------------------------------------------+ + /// ┌───────────────────────────────────────────────────────────────────────┐ + /// │ NEO Executable Format 3 (NEF3) │ + /// ├──────────┬───────────────┬────────────────────────────────────────────┤ + /// │ Field │ Type │ Comment │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Magic │ uint32 │ Magic header │ + /// │ Compiler │ byte[32] │ Compiler used │ + /// │ Version │ byte[32] │ Compiler version │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + /// │ Tokens │ MethodToken[] │ Method tokens. │ + /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + /// │ Script │ byte[] │ Var bytes for the payload │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Checksum │ uint32 │ First four bytes of double SHA256 hash │ + /// └──────────┴───────────────┴────────────────────────────────────────────┘ /// public class NefFile : ISerializable { @@ -35,6 +40,11 @@ public class NefFile : ISerializable /// public string Version { get; set; } + /// + /// Method tokens + /// + public MethodToken[] Tokens { get; set; } + /// /// Script /// @@ -53,12 +63,18 @@ public class NefFile : ISerializable public int Size => HeaderSize + // Header + 2 + // Reserve + Tokens.GetVarSize() + // Tokens + 2 + // Reserve Script.GetVarSize() + // Script sizeof(uint); // Checksum public void Serialize(BinaryWriter writer) { SerializeHeader(writer); + writer.Write((short)0); + writer.Write(Tokens); + writer.Write((short)0); writer.WriteVarBytes(Script ?? Array.Empty()); writer.Write(CheckSum); } @@ -78,6 +94,13 @@ public void Deserialize(BinaryReader reader) Compiler = reader.ReadFixedString(32); Version = reader.ReadFixedString(32); + + if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); + + Tokens = reader.ReadSerializableArray(128); + + if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); + Script = reader.ReadVarBytes(MaxScriptLength); if (Script.Length == 0) throw new ArgumentException($"Script can't be empty"); diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index dc666fab20..fc8cc50d60 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; +using System; namespace Neo.UnitTests.SmartContract { @@ -13,6 +14,7 @@ public void TestGetContractHash() { Compiler = "test", Version = new System.Version().ToString(), + Tokens = Array.Empty(), Script = new byte[] { 1, 2, 3 } }; nef.CheckSum = NefFile.ComputeChecksum(nef); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 09ab10c78b..211573ccd0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -136,7 +136,8 @@ public void TestContract_Create() { Script = new byte[byte.MaxValue], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); var nefFile = nef.ToArray(); @@ -149,7 +150,8 @@ public void TestContract_Create() { Script = new byte[NefFile.MaxScriptLength - 1], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(nef); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, script_exceedMaxLength.ToArray(), manifest.ToJson().ToByteArray(true))); @@ -181,7 +183,8 @@ public void TestContract_Update() { Script = new byte[] { 0x01 }, Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); Assert.ThrowsException(() => snapshot.UpdateContract(null, nef.ToArray(), new byte[0])); @@ -232,7 +235,8 @@ public void TestContract_Update_Invalid() { Script = new byte[] { 0x01 }, Version = new Version(1, 2, 3, 4).ToString(), - Compiler = "" + Compiler = "", + Tokens = System.Array.Empty() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); @@ -247,7 +251,8 @@ public void TestContract_Update_Invalid() { Script = new byte[0], Version = new Version(1, 2, 3, 4).ToString(), - Compiler = "" + Compiler = "", + Tokens = System.Array.Empty() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); diff --git a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs index 7a5412d980..575b4a0549 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs @@ -14,6 +14,7 @@ public class UT_NefFile { Compiler = "".PadLeft(32, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -78,7 +79,7 @@ public void TestDeserialize() [TestMethod] public void TestGetSize() { - file.Size.Should().Be(4 + 32 + 32 + 4 + 4); + file.Size.Should().Be(4 + 32 + 32 + 2 + 1 + 2 + 4 + 4); } [TestMethod] @@ -88,6 +89,7 @@ public void ParseTest() { Compiler = "".PadLeft(32, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -108,6 +110,7 @@ public void LimitTest() { Compiler = "".PadLeft(byte.MaxValue, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[1024 * 1024], CheckSum = 0 }; From aed0f60b1f06c00c0aa8c0427cd2175045a32658 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 28 Dec 2020 11:02:07 +0100 Subject: [PATCH 2/2] Add UT --- .../SmartContract/UT_MethodToken.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/neo.UnitTests/SmartContract/UT_MethodToken.cs diff --git a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs new file mode 100644 index 0000000000..e200da4f62 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs @@ -0,0 +1,51 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_MethodToken + { + [TestMethod] + public void TestSerialize() + { + var result = new MethodToken() + { + CallFlags = CallFlags.AllowCall, + Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Method = "myMethod", + ParametersCount = 123, + RVCount = 456 + }; + + var copy = result.ToArray().AsSerializable(); + + Assert.AreEqual(CallFlags.AllowCall, copy.CallFlags); + Assert.AreEqual("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", copy.Hash.ToString()); + Assert.AreEqual("myMethod", copy.Method); + Assert.AreEqual(123, copy.ParametersCount); + Assert.AreEqual(456, copy.RVCount); + } + + [TestMethod] + public void TestSerializeErrors() + { + var result = new MethodToken() + { + CallFlags = (CallFlags)byte.MaxValue, + Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Method = "myLongMethod", + ParametersCount = 123, + RVCount = 456 + }; + + Assert.ThrowsException(() => result.ToArray().AsSerializable()); + + result.CallFlags = CallFlags.All; + result.Method += "-123123123123123123123123"; + Assert.ThrowsException(() => result.ToArray().AsSerializable()); + } + } +}