diff --git a/src/Neo/SmartContract/ContractState.cs b/src/Neo/SmartContract/ContractState.cs index 83ac9d3353..5b413c41a4 100644 --- a/src/Neo/SmartContract/ContractState.cs +++ b/src/Neo/SmartContract/ContractState.cs @@ -23,7 +23,7 @@ namespace Neo.SmartContract /// /// Represents a deployed contract. /// - public class ContractState : IInteroperable + public class ContractState : IInteroperableVerifiable { /// /// The id of the contract. @@ -69,7 +69,7 @@ IInteroperable IInteroperable.Clone() void IInteroperable.FromReplica(IInteroperable replica) { - ContractState from = (ContractState)replica; + var from = (ContractState)replica; Id = from.Id; UpdateCounter = from.UpdateCounter; Hash = from.Hash; @@ -79,11 +79,16 @@ void IInteroperable.FromReplica(IInteroperable replica) void IInteroperable.FromStackItem(StackItem stackItem) { - Array array = (Array)stackItem; + ((IInteroperableVerifiable)this).FromStackItem(stackItem, true); + } + + void IInteroperableVerifiable.FromStackItem(StackItem stackItem, bool verify) + { + var array = (Array)stackItem; Id = (int)array[0].GetInteger(); UpdateCounter = (ushort)array[1].GetInteger(); Hash = new UInt160(array[2].GetSpan()); - Nef = ((ByteString)array[3]).Memory.AsSerializable(); + Nef = NefFile.Parse(((ByteString)array[3]).Memory, verify); Manifest = array[4].ToInteroperable(); } diff --git a/src/Neo/SmartContract/IInteroperableVerifiable.cs b/src/Neo/SmartContract/IInteroperableVerifiable.cs new file mode 100644 index 0000000000..c8af7b8a5e --- /dev/null +++ b/src/Neo/SmartContract/IInteroperableVerifiable.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IInteroperableVerifiable.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.VM.Types; + +namespace Neo.SmartContract +{ + /// + /// Represents the object that can be converted to and from + /// and allows you to specify whether a verification is required. + /// + public interface IInteroperableVerifiable : IInteroperable + { + /// + /// Convert a to the current object. + /// + /// The to convert. + /// Verify the content + void FromStackItem(StackItem stackItem, bool verify = true); + } +} diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs index 3347163a6c..28642a2db1 100644 --- a/src/Neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -84,7 +84,7 @@ internal override async ContractTask OnPersist(ApplicationEngine engine) else { // Parse old contract - var oldContract = state.GetInteroperable(); + var oldContract = state.GetInteroperable(false); // Increase the update counter oldContract.UpdateCounter++; // Modify nef and manifest @@ -122,7 +122,7 @@ private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public ContractState GetContract(DataCache snapshot, UInt160 hash) { - return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); + return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(false); } /// @@ -183,7 +183,7 @@ public bool HasMethod(DataCache snapshot, UInt160 hash, string method, int pcoun public IEnumerable ListContracts(DataCache snapshot) { byte[] listContractsPrefix = CreateStorageKey(Prefix_Contract).ToArray(); - return snapshot.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); + return snapshot.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable(false)); } [ContractMethod(RequiredCallFlags = CallFlags.All)] @@ -250,7 +250,7 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man engine.AddGas(engine.StoragePrice * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0))); - var contract = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(engine.CallingScriptHash))?.GetInteroperable(); + var contract = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(engine.CallingScriptHash))?.GetInteroperable(false); if (contract is null) throw new InvalidOperationException($"Updating Contract Does Not Exist: {engine.CallingScriptHash}"); if (contract.UpdateCounter == ushort.MaxValue) throw new InvalidOperationException($"The contract reached the maximum number of updates."); @@ -283,7 +283,7 @@ private void Destroy(ApplicationEngine engine) { UInt160 hash = engine.CallingScriptHash; StorageKey ckey = CreateStorageKey(Prefix_Contract).Add(hash); - ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable(); + ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable(false); if (contract is null) return; engine.Snapshot.Delete(ckey); engine.Snapshot.Delete(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id)); diff --git a/src/Neo/SmartContract/NefFile.cs b/src/Neo/SmartContract/NefFile.cs index 1ffa7baf20..3e343d3be2 100644 --- a/src/Neo/SmartContract/NefFile.cs +++ b/src/Neo/SmartContract/NefFile.cs @@ -86,6 +86,20 @@ public class NefFile : ISerializable Script.GetVarSize() + // Script sizeof(uint); // Checksum + /// + /// Parse NefFile from memory + /// + /// Memory + /// Do checksum and MaxItemSize checks + /// NefFile + public static NefFile Parse(ReadOnlyMemory memory, bool verify = true) + { + var reader = new MemoryReader(memory); + var nef = new NefFile(); + nef.Deserialize(ref reader, verify); + return nef; + } + public void Serialize(BinaryWriter writer) { SerializeHeader(writer); @@ -103,7 +117,9 @@ private void SerializeHeader(BinaryWriter writer) writer.WriteFixedString(Compiler, 64); } - public void Deserialize(ref MemoryReader reader) + public void Deserialize(ref MemoryReader reader) => Deserialize(ref reader, true); + + public void Deserialize(ref MemoryReader reader, bool verify = true) { long startPosition = reader.Position; if (reader.ReadUInt32() != Magic) throw new FormatException("Wrong magic"); @@ -115,8 +131,11 @@ public void Deserialize(ref MemoryReader reader) Script = reader.ReadVarMemory((int)ExecutionEngineLimits.Default.MaxItemSize); if (Script.Length == 0) throw new ArgumentException($"Script can't be empty"); CheckSum = reader.ReadUInt32(); - if (CheckSum != ComputeChecksum(this)) throw new FormatException("CRC verification fail"); - if (reader.Position - startPosition > ExecutionEngineLimits.Default.MaxItemSize) throw new FormatException("Max vm item size exceed"); + if (verify) + { + if (CheckSum != ComputeChecksum(this)) throw new FormatException("CRC verification fail"); + if (reader.Position - startPosition > ExecutionEngineLimits.Default.MaxItemSize) throw new FormatException("Max vm item size exceed"); + } } /// diff --git a/src/Neo/SmartContract/StorageItem.cs b/src/Neo/SmartContract/StorageItem.cs index 41486997de..350e95e70a 100644 --- a/src/Neo/SmartContract/StorageItem.cs +++ b/src/Neo/SmartContract/StorageItem.cs @@ -145,6 +145,24 @@ public void FromReplica(StorageItem replica) return (T)cache; } + /// + /// Gets an from the storage. + /// + /// Verify deserialization + /// The type of the . + /// The in the storage. + public T GetInteroperable(bool verify = true) where T : IInteroperableVerifiable, new() + { + if (cache is null) + { + var interoperable = new T(); + interoperable.FromStackItem(BinarySerializer.Deserialize(value, ExecutionEngineLimits.Default), verify); + cache = interoperable; + } + value = null; + return (T)cache; + } + public void Serialize(BinaryWriter writer) { writer.Write(Value.Span);