Skip to content

Commit

Permalink
Store NefFile in ContractState (neo-project#2195)
Browse files Browse the repository at this point in the history
* Store NefFile in ContractState

* deterministic

Co-authored-by: Shargon <shargon@gmail.com>
  • Loading branch information
2 people authored and cloud8little committed Jan 24, 2021
1 parent 1beb0a5 commit 8b5d3f7
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 43 deletions.
24 changes: 13 additions & 11 deletions src/neo/SmartContract/ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Neo.SmartContract.Manifest;
using Neo.VM;
using Neo.VM.Types;
using System;
using System.Linq;
using Array = Neo.VM.Types.Array;

Expand All @@ -14,16 +13,18 @@ public class ContractState : IInteroperable
public int Id;
public ushort UpdateCounter;
public UInt160 Hash;
public byte[] Script;
public NefFile Nef;
public ContractManifest Manifest;

public byte[] Script => Nef.Script;

void IInteroperable.FromStackItem(StackItem stackItem)
{
Array array = (Array)stackItem;
Id = (int)array[0].GetInteger();
UpdateCounter = (ushort)array[1].GetInteger();
Hash = new UInt160(array[2].GetSpan());
Script = array[3].GetSpan().ToArray();
Nef = array[3].GetSpan().AsSerializable<NefFile>();
Manifest = ContractManifest.Parse(array[4].GetSpan());
}

Expand All @@ -40,18 +41,19 @@ public bool CanCall(ContractState targetContract, string targetMethod)

public JObject ToJson()
{
JObject json = new JObject();
json["id"] = Id;
json["updatecounter"] = UpdateCounter;
json["hash"] = Hash.ToString();
json["script"] = Convert.ToBase64String(Script);
json["manifest"] = Manifest.ToJson();
return json;
return new JObject
{
["id"] = Id,
["updatecounter"] = UpdateCounter,
["hash"] = Hash.ToString(),
["nef"] = Nef.ToJson(),
["manifest"] = Manifest.ToJson()
};
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Script, Manifest.ToString() });
return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Nef.ToArray(), Manifest.ToString() });
}
}
}
25 changes: 19 additions & 6 deletions src/neo/SmartContract/MethodToken.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Neo.IO;
using Neo.IO.Json;
using System;
using System.IO;

Expand All @@ -9,34 +10,46 @@ public class MethodToken : ISerializable
public UInt160 Hash;
public string Method;
public ushort ParametersCount;
public ushort RVCount;
public bool HasReturnValue;
public CallFlags CallFlags;

public int Size =>
UInt160.Length + // Hash
Method.GetVarSize() + // Method
sizeof(ushort) + // ParametersCount
sizeof(ushort) + // RVCount
sizeof(bool) + // HasReturnValue
sizeof(CallFlags); // CallFlags

void ISerializable.Deserialize(BinaryReader reader)
{
Hash = reader.ReadSerializable<UInt160>();
Method = reader.ReadVarString(32);
if (Method.StartsWith('_')) throw new FormatException();
ParametersCount = reader.ReadUInt16();
RVCount = reader.ReadUInt16();
HasReturnValue = reader.ReadBoolean();
CallFlags = (CallFlags)reader.ReadByte();
if ((CallFlags & ~CallFlags.All) != 0)
throw new FormatException();
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(HasReturnValue);
writer.Write((byte)CallFlags);
}

public JObject ToJson()
{
return new JObject
{
["hash"] = Hash.ToString(),
["method"] = Method,
["paramcount"] = ParametersCount,
["hasreturnvalue"] = HasReturnValue,
["callflags"] = CallFlags
};
}
}
}
10 changes: 4 additions & 6 deletions src/neo/SmartContract/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ internal override void OnPersist(ApplicationEngine engine)
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState
{
Id = contract.Id,
Script = contract.Script,
Nef = contract.Nef,
Hash = contract.Hash,
Manifest = contract.Manifest
}));
Expand Down Expand Up @@ -145,7 +145,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma
{
Id = GetNextAvailableId(engine.Snapshot),
UpdateCounter = 0,
Script = nef.Script,
Nef = nef,
Hash = hash,
Manifest = ContractManifest.Parse(manifest)
};
Expand Down Expand Up @@ -180,10 +180,8 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest)
if (nefFile.Length == 0)
throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}");

NefFile nef = nefFile.AsSerializable<NefFile>();

// Update script
contract.Script = nef.Script;
// Update nef
contract.Nef = nefFile.AsSerializable<NefFile>();
}
if (manifest != null)
{
Expand Down
16 changes: 13 additions & 3 deletions src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,31 @@ public abstract class NativeContract
public static OracleContract Oracle { get; } = new OracleContract();

public string Name => GetType().Name;
public byte[] Script { get; }
public NefFile Nef { get; }
public byte[] Script => Nef.Script;
public UInt160 Hash { get; }
public abstract int Id { get; }
public ContractManifest Manifest { get; }
public uint ActiveBlockIndex { get; }

protected NativeContract()
{
byte[] script;
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitPush(Name);
sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative);
this.Script = sb.ToArray();
script = sb.ToArray();
}
this.Hash = Helper.GetContractHash(UInt160.Zero, Script);
this.Nef = new NefFile
{
Compiler = nameof(ScriptBuilder),
Version = "3.0",
Tokens = System.Array.Empty<MethodToken>(),
Script = script
};
this.Nef.CheckSum = NefFile.ComputeChecksum(Nef);
this.Hash = Helper.GetContractHash(UInt160.Zero, script);
List<ContractMethodDescriptor> descriptors = new List<ContractMethodDescriptor>();
foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
Expand Down
15 changes: 15 additions & 0 deletions src/neo/SmartContract/NefFile.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Neo.Cryptography;
using Neo.IO;
using Neo.IO.Json;
using System;
using System.IO;
using System.Linq;

namespace Neo.SmartContract
{
Expand Down Expand Up @@ -117,5 +119,18 @@ public static uint ComputeChecksum(NefFile file)
{
return BitConverter.ToUInt32(Crypto.Hash256(file.ToArray().AsSpan(..^sizeof(int))));
}

public JObject ToJson()
{
return new JObject
{
["magic"] = Magic,
["compiler"] = Compiler,
["version"] = Version,
["tokens"] = new JArray(Tokens.Select(p => p.ToJson())),
["script"] = Convert.ToBase64String(Script),
["checksum"] = CheckSum
};
}
}
}
13 changes: 11 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Neo.IO.Json;
using Neo.SmartContract;
using Neo.SmartContract.Manifest;
using Neo.VM;
using System;

namespace Neo.UnitTests.SmartContract
{
Expand All @@ -19,10 +21,17 @@ public void TestSetup()
manifest = TestUtils.CreateDefaultManifest();
contract = new ContractState
{
Script = script,
Nef = new NefFile
{
Compiler = nameof(ScriptBuilder),
Version = typeof(ScriptBuilder).Assembly.GetVersion(),
Tokens = Array.Empty<MethodToken>(),
Script = script
},
Hash = script.ToScriptHash(),
Manifest = manifest
};
contract.Nef.CheckSum = NefFile.ComputeChecksum(contract.Nef);
}

[TestMethod]
Expand Down Expand Up @@ -56,7 +65,7 @@ public void TestToJson()
{
JObject json = contract.ToJson();
json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5");
json["script"].AsString().Should().Be("AQ==");
json["nef"]["script"].AsString().Should().Be("AQ==");
json["manifest"].AsString().Should().Be(manifest.ToJson().AsString());
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void TestGetAddress()
}
}
},
Script = new byte[] { 1, 2, 3 },
Nef = new NefFile { Script = new byte[] { 1, 2, 3 } },
Hash = new byte[] { 1, 2, 3 }.ToScriptHash()
});

Expand Down Expand Up @@ -54,7 +54,7 @@ public void TestErrors()
}
}
},
Script = new byte[] { 1, 2, 3 }
Nef = new NefFile { Script = new byte[] { 1, 2, 3 } }
}));
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void TestAccount_IsStandard()
engine.LoadScript(new byte[] { 0x01 });
engine.IsStandardContract(state.Hash).Should().BeFalse();

state.Script = Contract.CreateSignatureRedeemScript(Blockchain.StandbyValidators[0]);
state.Nef.Script = Contract.CreateSignatureRedeemScript(Blockchain.StandbyValidators[0]);
engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot);
engine.LoadScript(new byte[] { 0x01 });
engine.IsStandardContract(state.Hash).Should().BeTrue();
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void Runtime_GetNotifications_Test()
snapshot.DeleteContract(scriptHash2);
snapshot.AddContract(scriptHash2, new ContractState()
{
Script = script.ToArray(),
Nef = new NefFile { Script = script.ToArray() },
Hash = script.ToArray().ToScriptHash(),
Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer),
});
Expand Down Expand Up @@ -217,7 +217,7 @@ public void TestExecutionEngine_GetCallingScriptHash()
var contract = new ContractState()
{
Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer),
Script = scriptA.ToArray(),
Nef = new NefFile { Script = scriptA.ToArray() },
Hash = scriptA.ToArray().ToScriptHash()
};
engine = GetEngine(true, true, false);
Expand Down
6 changes: 3 additions & 3 deletions tests/neo.UnitTests/SmartContract/UT_MethodToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void TestSerialize()
Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"),
Method = "myMethod",
ParametersCount = 123,
RVCount = 456
HasReturnValue = true
};

var copy = result.ToArray().AsSerializable<MethodToken>();
Expand All @@ -26,7 +26,7 @@ public void TestSerialize()
Assert.AreEqual("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", copy.Hash.ToString());
Assert.AreEqual("myMethod", copy.Method);
Assert.AreEqual(123, copy.ParametersCount);
Assert.AreEqual(456, copy.RVCount);
Assert.AreEqual(true, copy.HasReturnValue);
}

[TestMethod]
Expand All @@ -38,7 +38,7 @@ public void TestSerializeErrors()
Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"),
Method = "myLongMethod",
ParametersCount = 123,
RVCount = 456
HasReturnValue = true
};

Assert.ThrowsException<FormatException>(() => result.ToArray().AsSerializable<MethodToken>());
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void TestVerifyWitnesses()
};
snapshot3.AddContract(UInt160.Zero, new ContractState()
{
Script = Array.Empty<byte>(),
Nef = new NefFile { Script = Array.Empty<byte>() },
Hash = Array.Empty<byte>().ToScriptHash(),
Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature),
});
Expand All @@ -159,7 +159,7 @@ public void TestVerifyWitnesses()

var contract = new ContractState()
{
Script = "11".HexToBytes(), // 17 PUSH1
Nef = new NefFile { Script = "11".HexToBytes() }, // 17 PUSH1
Hash = "11".HexToBytes().ToScriptHash(),
Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), // Offset = 0
};
Expand Down
6 changes: 3 additions & 3 deletions tests/neo.UnitTests/SmartContract/UT_Syscalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,9 @@ public void System_Runtime_GetInvocationCounter()
{
script.EmitSysCall(ApplicationEngine.System_Runtime_GetInvocationCounter);

contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() };
contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() };
contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() };
contractA = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() } };
contractB = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } };
contractC = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } };
contractA.Hash = contractA.Script.ToScriptHash();
contractB.Hash = contractB.Script.ToScriptHash();
contractC.Hash = contractC.Script.ToScriptHash();
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ internal static ContractState GetContract(string method = "test", int parameters
return new ContractState
{
Id = 0x43000000,
Script = new byte[] { 0x01, 0x01, 0x01, 0x01 },
Nef = new NefFile { Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } },
Hash = new byte[] { 0x01, 0x01, 0x01, 0x01 }.ToScriptHash(),
Manifest = CreateManifest(method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray())
};
Expand All @@ -122,7 +122,7 @@ internal static ContractState GetContract(byte[] script)
return new ContractState
{
Id = 1,
Script = script,
Nef = new NefFile { Script = script },
Manifest = CreateDefaultManifest()
};
}
Expand Down

0 comments on commit 8b5d3f7

Please sign in to comment.