-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Script header * Script header draft * Clean files * Typo * Rename class and magic header * Clean hash * UT * Erik's suggestions * Change name * Typo * Check magic * 64 to 32 * Nef file only for neo-cli * Real Checksum * Clean * Clean again * Remove code * Update neo/SmartContract/NefFile.cs Co-Authored-By: Igor Machado Coelho <igor.machado@gmail.com> * Enum to const * Remove unnecessary change * Apply Erik's suggestions
- Loading branch information
Showing
2 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Neo.Cryptography; | ||
using Neo.IO; | ||
using Neo.SmartContract; | ||
using System; | ||
|
||
namespace Neo.UnitTests | ||
{ | ||
[TestClass] | ||
public class UT_NefFile | ||
{ | ||
[TestMethod] | ||
public void ParseTest() | ||
{ | ||
var file = new NefFile() | ||
{ | ||
Compiler = "".PadLeft(32, ' '), | ||
Version = new Version(1, 2, 3, 4), | ||
Script = new byte[] { 0x01, 0x02, 0x03 } | ||
}; | ||
|
||
file.ScriptHash = file.Script.ToScriptHash(); | ||
file.CheckSum = NefFile.ComputeChecksum(file); | ||
|
||
var data = file.ToArray(); | ||
file = data.AsSerializable<NefFile>(); | ||
|
||
Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); | ||
Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); | ||
Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); | ||
CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); | ||
} | ||
|
||
[TestMethod] | ||
public void LimitTest() | ||
{ | ||
var file = new NefFile() | ||
{ | ||
Compiler = "".PadLeft(byte.MaxValue, ' '), | ||
Version = new Version(1, 2, 3, 4), | ||
Script = new byte[1024 * 1024], | ||
ScriptHash = new byte[1024 * 1024].ToScriptHash(), | ||
CheckSum = 0 | ||
}; | ||
|
||
// Wrong compiler | ||
|
||
Assert.ThrowsException<ArgumentException>(() => file.ToArray()); | ||
|
||
// Wrong script | ||
|
||
file.Compiler = ""; | ||
file.Script = new byte[(1024 * 1024) + 1]; | ||
file.ScriptHash = file.Script.ToScriptHash(); | ||
var data = file.ToArray(); | ||
|
||
Assert.ThrowsException<FormatException>(() => data.AsSerializable<NefFile>()); | ||
|
||
// Wrong script hash | ||
|
||
file.Script = new byte[1024 * 1024]; | ||
data = file.ToArray(); | ||
|
||
Assert.ThrowsException<FormatException>(() => data.AsSerializable<NefFile>()); | ||
|
||
// Wrong checksum | ||
|
||
file.Script = new byte[1024]; | ||
data = file.ToArray(); | ||
file.CheckSum = NefFile.ComputeChecksum(file) + 1; | ||
|
||
Assert.ThrowsException<FormatException>(() => data.AsSerializable<NefFile>()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using Neo.Cryptography; | ||
using Neo.IO; | ||
using System; | ||
using System.IO; | ||
|
||
namespace Neo.SmartContract | ||
{ | ||
/// <summary> | ||
/// +------------+-----------+------------------------------------------------------------+ | ||
/// | Field | Length | Comment | | ||
/// +------------+-----------+------------------------------------------------------------+ | ||
/// | Magic | 4 bytes | Magic header | | ||
/// | Compiler | 32 bytes | Compiler used | | ||
/// | Version | 16 bytes | Compiler version (Mayor, Minor, Build, Version) | | ||
/// | ScriptHash | 20 bytes | ScriptHash for the script | | ||
/// +------------+-----------+------------------------------------------------------------+ | ||
/// | Checksum | 4 bytes | Sha256 of the header (CRC) | | ||
/// +------------+-----------+------------------------------------------------------------+ | ||
/// | Script | Var bytes | Var bytes for the payload | | ||
/// +------------+-----------+------------------------------------------------------------+ | ||
/// </summary> | ||
public class NefFile : ISerializable | ||
{ | ||
/// <summary> | ||
/// NEO Executable Format 3 (NEF3) | ||
/// </summary> | ||
private const uint Magic = 0x3346454E; | ||
|
||
/// <summary> | ||
/// Compiler | ||
/// </summary> | ||
public string Compiler { get; set; } | ||
|
||
/// <summary> | ||
/// Version | ||
/// </summary> | ||
public Version Version { get; set; } | ||
|
||
/// <summary> | ||
/// Script Hash | ||
/// </summary> | ||
public UInt160 ScriptHash { get; set; } | ||
|
||
/// <summary> | ||
/// Checksum | ||
/// </summary> | ||
public uint CheckSum { get; set; } | ||
|
||
/// <summary> | ||
/// Script | ||
/// </summary> | ||
public byte[] Script { get; set; } | ||
|
||
private const int HeaderSize = | ||
sizeof(uint) + // Magic | ||
32 + // Compiler | ||
(sizeof(int) * 4) + // Version | ||
UInt160.Length + // ScriptHash | ||
sizeof(uint); // Checksum | ||
|
||
public int Size => | ||
HeaderSize + // Header | ||
Script.GetVarSize(); // Script | ||
|
||
public void Serialize(BinaryWriter writer) | ||
{ | ||
writer.Write(Magic); | ||
writer.WriteFixedString(Compiler, 32); | ||
|
||
// Version | ||
writer.Write(Version.Major); | ||
writer.Write(Version.Minor); | ||
writer.Write(Version.Build); | ||
writer.Write(Version.Revision); | ||
|
||
writer.Write(ScriptHash); | ||
writer.Write(CheckSum); | ||
writer.WriteVarBytes(Script ?? new byte[0]); | ||
} | ||
|
||
public void Deserialize(BinaryReader reader) | ||
{ | ||
if (reader.ReadUInt32() != Magic) | ||
{ | ||
throw new FormatException("Wrong magic"); | ||
} | ||
|
||
Compiler = reader.ReadFixedString(32); | ||
Version = new Version(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); | ||
ScriptHash = reader.ReadSerializable<UInt160>(); | ||
CheckSum = reader.ReadUInt32(); | ||
|
||
if (CheckSum != ComputeChecksum(this)) | ||
{ | ||
throw new FormatException("CRC verification fail"); | ||
} | ||
|
||
Script = reader.ReadVarBytes(1024 * 1024); | ||
|
||
if (Script.ToScriptHash() != ScriptHash) | ||
{ | ||
throw new FormatException("ScriptHash is different"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Compute checksum for a file | ||
/// </summary> | ||
/// <param name="file">File</param> | ||
/// <returns>Return checksum</returns> | ||
public static uint ComputeChecksum(NefFile file) | ||
{ | ||
using (var ms = new MemoryStream()) | ||
using (var wr = new BinaryWriter(ms)) | ||
{ | ||
file.Serialize(wr); | ||
wr.Flush(); | ||
|
||
// Read header without CRC | ||
|
||
var buffer = new byte[HeaderSize - sizeof(uint)]; | ||
ms.Seek(0, SeekOrigin.Begin); | ||
ms.Read(buffer, 0, buffer.Length); | ||
|
||
return BitConverter.ToUInt32(buffer.Sha256(), 0); | ||
} | ||
} | ||
} | ||
} |