diff --git a/src/Neo.VM/JumpTable/JumpTable.Splice.cs b/src/Neo.VM/JumpTable/JumpTable.Splice.cs index 5461518516..6b2124fab8 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Splice.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Splice.cs @@ -101,7 +101,7 @@ public virtual void SubStr(ExecutionEngine engine, Instruction instruction) if (index < 0) throw new InvalidOperationException($"The index can not be negative for {nameof(OpCode.SUBSTR)}, index: {index}."); var x = engine.Pop().GetSpan(); - if (index + count > x.Length) + if (checked(index + count) > x.Length) throw new InvalidOperationException($"The index + count is out of range for {nameof(OpCode.SUBSTR)}, index: {index}, count: {count}, {index + count}/[0, {x.Length}]."); Types.Buffer result = new(count, false); x.Slice(index, count).CopyTo(result.InnerBuffer.Span); diff --git a/src/Neo/Cryptography/Ed25519.cs b/src/Neo/Cryptography/Ed25519.cs new file mode 100644 index 0000000000..585970a4fc --- /dev/null +++ b/src/Neo/Cryptography/Ed25519.cs @@ -0,0 +1,99 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// Ed25519.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 Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; +using System; + +namespace Neo.Cryptography +{ + public class Ed25519 + { + internal const int PublicKeySize = 32; + private const int PrivateKeySize = 32; + internal const int SignatureSize = 64; + + /// + /// Generates a new Ed25519 key pair. + /// + /// A byte array containing the private key. + public static byte[] GenerateKeyPair() + { + var keyPairGenerator = new Ed25519KeyPairGenerator(); + keyPairGenerator.Init(new Ed25519KeyGenerationParameters(new SecureRandom())); + var keyPair = keyPairGenerator.GenerateKeyPair(); + return ((Ed25519PrivateKeyParameters)keyPair.Private).GetEncoded(); + } + + /// + /// Derives the public key from a given private key. + /// + /// The private key as a byte array. + /// The corresponding public key as a byte array. + /// Thrown when the private key size is invalid. + public static byte[] GetPublicKey(byte[] privateKey) + { + if (privateKey.Length != PrivateKeySize) + throw new ArgumentException("Invalid private key size", nameof(privateKey)); + + var privateKeyParams = new Ed25519PrivateKeyParameters(privateKey, 0); + return privateKeyParams.GeneratePublicKey().GetEncoded(); + } + + /// + /// Signs a message using the provided private key. + /// Parameters are in the same order as the sample in the Ed25519 specification + /// Ed25519.sign(privkey, pubkey, msg) with pubkey omitted + /// ref. https://datatracker.ietf.org/doc/html/rfc8032. + /// + /// The private key used for signing. + /// The message to be signed. + /// The signature as a byte array. + /// Thrown when the private key size is invalid. + public static byte[] Sign(byte[] privateKey, byte[] message) + { + if (privateKey.Length != PrivateKeySize) + throw new ArgumentException("Invalid private key size", nameof(privateKey)); + + var signer = new Ed25519Signer(); + signer.Init(true, new Ed25519PrivateKeyParameters(privateKey, 0)); + signer.BlockUpdate(message, 0, message.Length); + return signer.GenerateSignature(); + } + + /// + /// Verifies an Ed25519 signature for a given message using the provided public key. + /// Parameters are in the same order as the sample in the Ed25519 specification + /// Ed25519.verify(public, msg, signature) + /// ref. https://datatracker.ietf.org/doc/html/rfc8032. + /// + /// The 32-byte public key used for verification. + /// The message that was signed. + /// The 64-byte signature to verify. + /// True if the signature is valid for the given message and public key; otherwise, false. + /// Thrown when the signature or public key size is invalid. + public static bool Verify(byte[] publicKey, byte[] message, byte[] signature) + { + if (signature.Length != SignatureSize) + throw new ArgumentException("Invalid signature size", nameof(signature)); + + if (publicKey.Length != PublicKeySize) + throw new ArgumentException("Invalid public key size", nameof(publicKey)); + + var verifier = new Ed25519Signer(); + verifier.Init(false, new Ed25519PublicKeyParameters(publicKey, 0)); + verifier.BlockUpdate(message, 0, message.Length); + return verifier.VerifySignature(signature); + } + } +} diff --git a/src/Neo/Hardfork.cs b/src/Neo/Hardfork.cs index 76e8024a99..f99c09ea2d 100644 --- a/src/Neo/Hardfork.cs +++ b/src/Neo/Hardfork.cs @@ -16,6 +16,7 @@ public enum Hardfork : byte HF_Aspidochelone, HF_Basilisk, HF_Cockatrice, - HF_Domovoi + HF_Domovoi, + HF_Echidna } } diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj index 5092f442fc..49dba91020 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -16,6 +16,8 @@ + + diff --git a/src/Neo/ProtocolSettings.cs b/src/Neo/ProtocolSettings.cs index d372ed52e4..f07df4761f 100644 --- a/src/Neo/ProtocolSettings.cs +++ b/src/Neo/ProtocolSettings.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; namespace Neo @@ -123,19 +124,69 @@ public record ProtocolSettings public static ProtocolSettings Custom { get; set; } + /// + /// Searches for a file in the given path. If not found, checks in the executable directory. + /// + /// The name of the file to search for. + /// The primary path to search in. + /// Full path of the file if found, null otherwise. + public static string FindFile(string fileName, string path) + { + // Check if the given path is relative + if (!Path.IsPathRooted(path)) + { + // Combine with the executable directory if relative + var executablePath = AppContext.BaseDirectory; + path = Path.Combine(executablePath, path); + } + + // Check if file exists in the specified (resolved) path + var fullPath = Path.Combine(path, fileName); + if (File.Exists(fullPath)) + { + return fullPath; + } + + // Check if file exists in the executable directory + var executableDir = AppContext.BaseDirectory; + fullPath = Path.Combine(executableDir, fileName); + if (File.Exists(fullPath)) + { + return fullPath; + } + + // File not found in either location + return null; + } + + /// + /// Loads the from the specified stream. + /// + /// The stream of the settings. + /// The loaded . + public static ProtocolSettings Load(Stream stream) + { + var config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + var section = config.GetSection("ProtocolConfiguration"); + return Load(section); + } + /// /// Loads the at the specified path. /// /// The path of the settings file. - /// Indicates whether the file is optional. /// The loaded . - public static ProtocolSettings Load(string path, bool optional = true) + public static ProtocolSettings Load(string path) { - IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile(path, optional).Build(); - IConfigurationSection section = config.GetSection("ProtocolConfiguration"); - var settings = Load(section); - CheckingHardfork(settings); - return settings; + path = FindFile(path, Environment.CurrentDirectory); + + if (path is null) + { + return Default; + } + + using var stream = File.OpenRead(path); + return Load(stream); } /// @@ -165,6 +216,7 @@ public static ProtocolSettings Load(IConfigurationSection section) ? EnsureOmmitedHardforks(section.GetSection("Hardforks").GetChildren().ToDictionary(p => Enum.Parse(p.Key, true), p => uint.Parse(p.Value))).ToImmutableDictionary() : Default.Hardforks }; + CheckingHardfork(Custom); return Custom; } diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index 00babcd784..0f22b158d9 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -35,6 +35,7 @@ namespace Neo.SmartContract public partial class ApplicationEngine : ExecutionEngine { protected static readonly JumpTable DefaultJumpTable = ComposeDefaultJumpTable(); + protected static readonly JumpTable NotEchidnaJumpTable = ComposeNotEchidnaJumpTable(); /// /// The maximum cost that can be spent when a contract is executed in test mode. @@ -215,6 +216,13 @@ private static JumpTable ComposeDefaultJumpTable() return table; } + public static JumpTable ComposeNotEchidnaJumpTable() + { + var jumpTable = ComposeDefaultJumpTable(); + jumpTable[OpCode.SUBSTR] = VulnerableSubStr; + return jumpTable; + } + protected static void OnCallT(ExecutionEngine engine, Instruction instruction) { if (engine is ApplicationEngine app) @@ -400,13 +408,41 @@ internal override void UnloadContext(ExecutionContext context) /// The engine instance created. public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null) { + var index = persistingBlock?.Index ?? (snapshot == null ? 0 : NativeContract.Ledger.CurrentIndex(snapshot)); + // Adjust jump table according persistingBlock - var jumpTable = ApplicationEngine.DefaultJumpTable; + var jumpTable = settings == null || settings.IsHardforkEnabled(Hardfork.HF_Echidna, index) ? DefaultJumpTable : NotEchidnaJumpTable; return Provider?.Create(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable) ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable); } + /// + /// Extracts a substring from the specified buffer and pushes it onto the evaluation stack. + /// + /// + /// The execution engine. + /// The instruction being executed. + /// Pop 3, Push 1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void VulnerableSubStr(ExecutionEngine engine, Instruction instruction) + { + var count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The count can not be negative for {nameof(OpCode.SUBSTR)}, count: {count}."); + var index = (int)engine.Pop().GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The index can not be negative for {nameof(OpCode.SUBSTR)}, index: {index}."); + var x = engine.Pop().GetSpan(); + // Note: here it's the main change + if (index + count > x.Length) + throw new InvalidOperationException($"The index + count is out of range for {nameof(OpCode.SUBSTR)}, index: {index}, count: {count}, {index + count}/[0, {x.Length}]."); + + VM.Types.Buffer result = new(count, false); + x.Slice(index, count).CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + public override void LoadContext(ExecutionContext context) { // Set default execution context state diff --git a/src/Neo/SmartContract/Contract.cs b/src/Neo/SmartContract/Contract.cs index d851e436a0..1e0d2ed27d 100644 --- a/src/Neo/SmartContract/Contract.cs +++ b/src/Neo/SmartContract/Contract.cs @@ -103,7 +103,7 @@ public static Contract CreateMultiSigContract(int m, IReadOnlyCollection publicKeys) { if (!(1 <= m && m <= publicKeys.Count && publicKeys.Count <= 1024)) - throw new ArgumentException(); + throw new ArgumentException($"Invalid multisig parameters: m={m}, publicKeys.Count={publicKeys.Count}"); using ScriptBuilder sb = new(); sb.EmitPush(m); foreach (ECPoint publicKey in publicKeys.OrderBy(p => p)) diff --git a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs index c909a47bb4..1df4de7d95 100644 --- a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs @@ -15,7 +15,8 @@ namespace Neo.SmartContract.Native { [DebuggerDisplay("{Name}")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] + // We allow multiple attributes because the fees or requiredCallFlags may change between hard forks. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] internal class ContractMethodAttribute : Attribute, IHardforkActivable { public string Name { get; init; } diff --git a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs index 14996a3da6..9b42f59231 100644 --- a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs @@ -40,7 +40,8 @@ internal class ContractMethodMetadata : IHardforkActivable public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute) { - Name = attribute.Name ?? member.Name.ToLower()[0] + member.Name[1..]; + Name = attribute.Name ?? member.Name; + Name = Name.ToLowerInvariant()[0] + Name[1..]; Handler = member switch { MethodInfo m => m, diff --git a/src/Neo/SmartContract/Native/CryptoLib.cs b/src/Neo/SmartContract/Native/CryptoLib.cs index 3461a3f198..219b19f657 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.cs @@ -11,6 +11,8 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; using System; using System.Collections.Generic; @@ -115,5 +117,34 @@ public static bool VerifyWithECDsaV0(byte[] message, byte[] pubkey, byte[] signa return false; } } + + /// + /// Verifies that a digital signature is appropriate for the provided key and message using the Ed25519 algorithm. + /// + /// The signed message. + /// The Ed25519 public key to be used. + /// The signature to be verified. + /// if the signature is valid; otherwise, . + [ContractMethod(Hardfork.HF_Echidna, CpuFee = 1 << 15)] + public static bool VerifyWithEd25519(byte[] message, byte[] publicKey, byte[] signature) + { + if (signature.Length != Ed25519.SignatureSize) + return false; + + if (publicKey.Length != Ed25519.PublicKeySize) + return false; + + try + { + var verifier = new Ed25519Signer(); + verifier.Init(false, new Ed25519PublicKeyParameters(publicKey, 0)); + verifier.BlockUpdate(message, 0, message.Length); + return verifier.VerifySignature(signature); + } + catch (Exception) + { + return false; + } + } } } diff --git a/src/Neo/SmartContract/Native/FungibleToken.cs b/src/Neo/SmartContract/Native/FungibleToken.cs index a4b0b8a128..3171c84d99 100644 --- a/src/Neo/SmartContract/Native/FungibleToken.cs +++ b/src/Neo/SmartContract/Native/FungibleToken.cs @@ -65,7 +65,7 @@ protected FungibleToken() : base() Factor = BigInteger.Pow(10, Decimals); } - protected override void OnManifestCompose(ContractManifest manifest) + protected override void OnManifestCompose(IsHardforkEnabledDelegate hfChecker, uint blockHeight, ContractManifest manifest) { manifest.SupportedStandards = new[] { "NEP-17" }; } diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index 77dafc7d05..11c43220bd 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -140,11 +140,12 @@ protected NativeContract() // Reflection to get the methods List listMethods = []; - foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) + foreach (var member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { - ContractMethodAttribute attribute = member.GetCustomAttribute(); - if (attribute is null) continue; - listMethods.Add(new ContractMethodMetadata(member, attribute)); + foreach (var attribute in member.GetCustomAttributes()) + { + listMethods.Add(new ContractMethodMetadata(member, attribute)); + } } _methodDescriptors = listMethods.OrderBy(p => p.Name, StringComparer.Ordinal).ThenBy(p => p.Parameters.Length).ToList().AsReadOnly(); @@ -258,7 +259,7 @@ public ContractState GetContractState(IsHardforkEnabledDelegate hfChecker, uint Extra = null }; - OnManifestCompose(manifest); + OnManifestCompose(hfChecker, blockHeight, manifest); // Return ContractState return new ContractState @@ -270,7 +271,7 @@ public ContractState GetContractState(IsHardforkEnabledDelegate hfChecker, uint }; } - protected virtual void OnManifestCompose(ContractManifest manifest) { } + protected virtual void OnManifestCompose(IsHardforkEnabledDelegate hfChecker, uint blockHeight, ContractManifest manifest) { } /// /// It is the initialize block @@ -363,6 +364,13 @@ public static NativeContract GetContract(UInt160 hash) return contract; } + internal Dictionary GetContractMethods(ApplicationEngine engine) + { + var nativeContracts = engine.GetState(() => new NativeContractsCache()); + var currentAllowedMethods = nativeContracts.GetAllowedMethods(this, engine); + return currentAllowedMethods.Methods; + } + internal async void Invoke(ApplicationEngine engine, byte version) { try @@ -370,16 +378,15 @@ internal async void Invoke(ApplicationEngine engine, byte version) if (version != 0) throw new InvalidOperationException($"The native contract of version {version} is not active."); // Get native contracts invocation cache - NativeContractsCache nativeContracts = engine.GetState(() => new NativeContractsCache()); - NativeContractsCache.CacheEntry currentAllowedMethods = nativeContracts.GetAllowedMethods(this, engine); + var currentAllowedMethods = GetContractMethods(engine); // Check if the method is allowed - ExecutionContext context = engine.CurrentContext; - ContractMethodMetadata method = currentAllowedMethods.Methods[context.InstructionPointer]; + var context = engine.CurrentContext; + var method = currentAllowedMethods[context.InstructionPointer]; if (method.ActiveIn is not null && !engine.IsHardforkEnabled(method.ActiveIn.Value)) throw new InvalidOperationException($"Cannot call this method before hardfork {method.ActiveIn}."); if (method.DeprecatedIn is not null && engine.IsHardforkEnabled(method.DeprecatedIn.Value)) throw new InvalidOperationException($"Cannot call this method after hardfork {method.DeprecatedIn}."); - ExecutionContextState state = context.GetState(); + var state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); // In the unit of datoshi, 1 datoshi = 1e-8 GAS diff --git a/src/Neo/SmartContract/Native/NeoToken.cs b/src/Neo/SmartContract/Native/NeoToken.cs index c72220b833..600bb5c3fe 100644 --- a/src/Neo/SmartContract/Native/NeoToken.cs +++ b/src/Neo/SmartContract/Native/NeoToken.cs @@ -16,6 +16,7 @@ using Neo.IO; using Neo.Persistence; using Neo.SmartContract.Iterators; +using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; using System; @@ -107,6 +108,18 @@ private protected override async ContractTask PostTransferAsync(ApplicationEngin await GAS.Mint(engine, distribution.Account, distribution.Amount, callOnPayment); } + protected override void OnManifestCompose(IsHardforkEnabledDelegate hfChecker, uint blockHeight, ContractManifest manifest) + { + if (hfChecker(Hardfork.HF_Echidna, blockHeight)) + { + manifest.SupportedStandards = new[] { "NEP-17", "NEP-27" }; + } + else + { + manifest.SupportedStandards = new[] { "NEP-17" }; + } + } + private GasDistribution DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { // PersistingBlock is null when running under the debugger @@ -332,13 +345,42 @@ public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state, end); } - [ContractMethod(RequiredCallFlags = CallFlags.States)] + [ContractMethod(Hardfork.HF_Echidna, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] + private async ContractTask OnNEP17Payment(ApplicationEngine engine, UInt160 from, BigInteger amount, StackItem data) + { + if (engine.CallingScriptHash != GAS.Hash) + throw new InvalidOperationException("only GAS is accepted"); + + if ((long)amount != GetRegisterPrice(engine.SnapshotCache)) + throw new ArgumentException("incorrect GAS amount for registration"); + + var pubkey = ECPoint.DecodePoint(data.GetSpan(), ECCurve.Secp256r1); + + if (!RegisterInternal(engine, pubkey)) + throw new InvalidOperationException("failed to register candidate"); + + await GAS.Burn(engine, Hash, amount); + } + + [ContractMethod(true, Hardfork.HF_Echidna, RequiredCallFlags = CallFlags.States)] + [ContractMethod(Hardfork.HF_Echidna, /* */ RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { - if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + // This check can be removed post-Echidna if compatible, + // RegisterInternal does this anyway. + var index = engine.PersistingBlock?.Index ?? Ledger.CurrentIndex(engine.SnapshotCache); + if (!engine.ProtocolSettings.IsHardforkEnabled(Hardfork.HF_Echidna, index) && + !engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; // In the unit of datoshi, 1 datoshi = 1e-8 GAS engine.AddFee(GetRegisterPrice(engine.SnapshotCache)); + return RegisterInternal(engine, pubkey); + } + + private bool RegisterInternal(ApplicationEngine engine, ECPoint pubkey) + { + if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + return false; StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey); StorageItem item = engine.SnapshotCache.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); @@ -349,7 +391,8 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] + [ContractMethod(true, Hardfork.HF_Echidna, CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] + [ContractMethod(Hardfork.HF_Echidna, /* */ CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) @@ -366,7 +409,8 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] + [ContractMethod(true, Hardfork.HF_Echidna, CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] + [ContractMethod(Hardfork.HF_Echidna, /* */ CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private async ContractTask Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { if (!engine.CheckWitnessInternal(account)) return false; diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index cf78af59a9..0822dc265f 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -26,7 +26,16 @@ public sealed class RoleManagement : NativeContract { [ContractEvent(0, name: "Designation", "Role", ContractParameterType.Integer, - "BlockIndex", ContractParameterType.Integer)] + "BlockIndex", ContractParameterType.Integer, + Hardfork.HF_Echidna)] + + [ContractEvent(Hardfork.HF_Echidna, 0, name: "Designation", + "Role", ContractParameterType.Integer, + "BlockIndex", ContractParameterType.Integer, + "Old", ContractParameterType.Array, + "New", ContractParameterType.Array + )] + internal RoleManagement() : base() { } /// @@ -69,7 +78,17 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node list.AddRange(nodes); list.Sort(); engine.SnapshotCache.Add(key, new StorageItem(list)); - engine.SendNotification(Hash, "Designation", new VM.Types.Array(engine.ReferenceCounter, new StackItem[] { (int)role, engine.PersistingBlock.Index })); + if (engine.IsHardforkEnabled(Hardfork.HF_Echidna)) + { + var oldNodes = new VM.Types.Array(engine.ReferenceCounter, GetDesignatedByRole(engine.SnapshotCache, role, index - 1).Select(u => (ByteString)u.EncodePoint(true))); + var newNodes = new VM.Types.Array(engine.ReferenceCounter, nodes.Select(u => (ByteString)u.EncodePoint(true))); + + engine.SendNotification(Hash, "Designation", new VM.Types.Array(engine.ReferenceCounter, [(int)role, engine.PersistingBlock.Index, oldNodes, newNodes])); + } + else + { + engine.SendNotification(Hash, "Designation", new VM.Types.Array(engine.ReferenceCounter, [(int)role, engine.PersistingBlock.Index])); + } } private class NodeList : InteroperableList diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index e1880d20a6..8a1421c113 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -11,6 +11,7 @@ #pragma warning disable IDE0051 +using Microsoft.IdentityModel.Tokens; using Neo.Cryptography; using Neo.Json; using Neo.VM.Types; @@ -131,6 +132,28 @@ public static byte[] Base64Decode([MaxLength(MaxInputLength)] string s) return Convert.FromBase64String(s); } + /// + /// Encodes a byte array into a base64Url string. + /// + /// The base64Url to be encoded. + /// The encoded base64Url string. + [ContractMethod(Hardfork.HF_Echidna, CpuFee = 1 << 5)] + public static string Base64UrlEncode([MaxLength(MaxInputLength)] string data) + { + return Base64UrlEncoder.Encode(data); + } + + /// + /// Decodes a byte array from a base64Url string. + /// + /// The base64Url string. + /// The decoded base64Url string. + [ContractMethod(Hardfork.HF_Echidna, CpuFee = 1 << 5)] + public static string Base64UrlDecode([MaxLength(MaxInputLength)] string s) + { + return Base64UrlEncoder.Decode(s); + } + /// /// Encodes a byte array into a base58 . /// diff --git a/src/Plugins/StatelessBlock/StatelessBlock.cs b/src/Plugins/StatelessBlock/StatelessBlock.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Plugins/StatelessBlock/StatelessBlock.csproj b/src/Plugins/StatelessBlock/StatelessBlock.csproj new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/Neo.UnitTests/Cryptography/UT_Ed25519.cs b/tests/Neo.UnitTests/Cryptography/UT_Ed25519.cs new file mode 100644 index 0000000000..940061c5c6 --- /dev/null +++ b/tests/Neo.UnitTests/Cryptography/UT_Ed25519.cs @@ -0,0 +1,162 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// UT_Ed25519.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Extensions; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; +using System.Linq; +using System.Text; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_Ed25519 + { + [TestMethod] + public void TestGenerateKeyPair() + { + byte[] keyPair = Ed25519.GenerateKeyPair(); + keyPair.Should().NotBeNull(); + keyPair.Length.Should().Be(32); + } + + [TestMethod] + public void TestGetPublicKey() + { + byte[] privateKey = Ed25519.GenerateKeyPair(); + byte[] publicKey = Ed25519.GetPublicKey(privateKey); + publicKey.Should().NotBeNull(); + publicKey.Length.Should().Be(Ed25519.PublicKeySize); + } + + [TestMethod] + public void TestSignAndVerify() + { + byte[] privateKey = Ed25519.GenerateKeyPair(); + byte[] publicKey = Ed25519.GetPublicKey(privateKey); + byte[] message = Encoding.UTF8.GetBytes("Hello, Neo!"); + + byte[] signature = Ed25519.Sign(privateKey, message); + signature.Should().NotBeNull(); + signature.Length.Should().Be(Ed25519.SignatureSize); + + bool isValid = Ed25519.Verify(publicKey, message, signature); + isValid.Should().BeTrue(); + } + + [TestMethod] + public void TestFailedVerify() + { + byte[] privateKey = Ed25519.GenerateKeyPair(); + byte[] publicKey = Ed25519.GetPublicKey(privateKey); + byte[] message = Encoding.UTF8.GetBytes("Hello, Neo!"); + + byte[] signature = Ed25519.Sign(privateKey, message); + + // Tamper with the message + byte[] tamperedMessage = Encoding.UTF8.GetBytes("Hello, Neo?"); + + bool isValid = Ed25519.Verify(publicKey, tamperedMessage, signature); + isValid.Should().BeFalse(); + + // Tamper with the signature + byte[] tamperedSignature = new byte[signature.Length]; + Array.Copy(signature, tamperedSignature, signature.Length); + tamperedSignature[0] ^= 0x01; // Flip one bit + + isValid = Ed25519.Verify(publicKey, message, tamperedSignature); + isValid.Should().BeFalse(); + + // Use wrong public key + byte[] wrongPrivateKey = Ed25519.GenerateKeyPair(); + byte[] wrongPublicKey = Ed25519.GetPublicKey(wrongPrivateKey); + + isValid = Ed25519.Verify(wrongPublicKey, message, signature); + isValid.Should().BeFalse(); + } + + [TestMethod] + public void TestInvalidPrivateKeySize() + { + byte[] invalidPrivateKey = new byte[31]; // Invalid size + Action act = () => Ed25519.GetPublicKey(invalidPrivateKey); + act.Should().Throw().WithMessage("Invalid private key size*"); + } + + [TestMethod] + public void TestInvalidSignatureSize() + { + byte[] message = Encoding.UTF8.GetBytes("Test message"); + byte[] invalidSignature = new byte[63]; // Invalid size + byte[] publicKey = new byte[Ed25519.PublicKeySize]; + Action act = () => Ed25519.Verify(publicKey, message, invalidSignature); + act.Should().Throw().WithMessage("Invalid signature size*"); + } + + [TestMethod] + public void TestInvalidPublicKeySize() + { + byte[] message = Encoding.UTF8.GetBytes("Test message"); + byte[] signature = new byte[Ed25519.SignatureSize]; + byte[] invalidPublicKey = new byte[31]; // Invalid size + Action act = () => Ed25519.Verify(invalidPublicKey, message, signature); + act.Should().Throw().WithMessage("Invalid public key size*"); + } + + // Test vectors from RFC 8032 (https://datatracker.ietf.org/doc/html/rfc8032) + // Section 7.1. Test Vectors for Ed25519 + + [TestMethod] + public void TestVectorCase1() + { + byte[] privateKey = "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60".HexToBytes(); + byte[] publicKey = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".HexToBytes(); + byte[] message = Array.Empty(); + byte[] signature = ("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" + + "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b").HexToBytes(); + + Ed25519.GetPublicKey(privateKey).Should().Equal(publicKey); + Ed25519.Sign(privateKey, message).Should().Equal(signature); + } + + [TestMethod] + public void TestVectorCase2() + { + byte[] privateKey = "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb".HexToBytes(); + byte[] publicKey = "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c".HexToBytes(); + byte[] message = Encoding.UTF8.GetBytes("r"); + byte[] signature = ("92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" + + "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00").HexToBytes(); + + Ed25519.GetPublicKey(privateKey).Should().Equal(publicKey); + Ed25519.Sign(privateKey, message).Should().Equal(signature); + } + + [TestMethod] + public void TestVectorCase3() + { + byte[] privateKey = "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7".HexToBytes(); + byte[] publicKey = "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025".HexToBytes(); + byte[] signature = ("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac" + + "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").HexToBytes(); + byte[] message = "af82".HexToBytes(); + Ed25519.GetPublicKey(privateKey).Should().Equal(publicKey); + Ed25519.Sign(privateKey, message).Should().Equal(signature); + } + } +} diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index c1dc80e19d..15836c45d1 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; namespace Neo.UnitTests.SmartContract.Native { @@ -910,5 +911,59 @@ private bool CallVerifyWithECDsa(byte[] message, ECPoint pub, byte[] signature, return engine.ResultStack.Pop().GetBoolean(); } } + + [TestMethod] + public void TestVerifyWithEd25519() + { + byte[] privateKey = "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60".HexToBytes(); + byte[] publicKey = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".HexToBytes(); + byte[] message = Array.Empty(); + byte[] signature = ("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" + + "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b").HexToBytes(); + + // Verify using Ed25519 directly + Ed25519.Verify(publicKey, message, signature).Should().BeTrue(); + + // Verify using CryptoLib.VerifyWithEd25519 + CallVerifyWithEd25519(message, publicKey, signature).Should().BeTrue(); + + // Test with a different message + byte[] differentMessage = Encoding.UTF8.GetBytes("Different message"); + CallVerifyWithEd25519(differentMessage, publicKey, signature).Should().BeFalse(); + + // Test with an invalid signature + byte[] invalidSignature = new byte[signature.Length]; + Array.Copy(signature, invalidSignature, signature.Length); + invalidSignature[0] ^= 0x01; // Flip one bit + CallVerifyWithEd25519(message, publicKey, invalidSignature).Should().BeFalse(); + + // Test with an invalid public key + byte[] invalidPublicKey = new byte[publicKey.Length]; + Array.Copy(publicKey, invalidPublicKey, publicKey.Length); + invalidPublicKey[0] ^= 0x01; // Flip one bit + CallVerifyWithEd25519(message, invalidPublicKey, signature).Should().BeFalse(); + } + + private bool CallVerifyWithEd25519(byte[] message, byte[] publicKey, byte[] signature) + { + var snapshot = TestBlockchain.GetTestSnapshotCache(); + using (ScriptBuilder script = new()) + { + script.EmitPush(signature); + script.EmitPush(publicKey); + script.EmitPush(message); + script.EmitPush(3); + script.Emit(OpCode.PACK); + script.EmitPush(CallFlags.All); + script.EmitPush("verifyWithEd25519"); + script.EmitPush(NativeContract.CryptoLib.Hash); + script.EmitSysCall(ApplicationEngine.System_Contract_Call); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + return engine.ResultStack.Pop().GetBoolean(); + } + } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 82d488f8fb..02c4d76c0e 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -42,13 +42,13 @@ public void TestSetup() _nativeStates = new Dictionary { {"ContractManagement", """{"id":-1,"updatecounter":0,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"ContractManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","offset":0,"safe":false},{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","offset":7,"safe":false},{"name":"destroy","parameters":[],"returntype":"Void","offset":14,"safe":false},{"name":"getContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","offset":21,"safe":true},{"name":"getContractById","parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getContractHashes","parameters":[],"returntype":"InteropInterface","offset":35,"safe":true},{"name":"getMinimumDeploymentFee","parameters":[],"returntype":"Integer","offset":42,"safe":true},{"name":"hasMethod","parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","offset":49,"safe":true},{"name":"setMinimumDeploymentFee","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":56,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","offset":63,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","offset":70,"safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}""" }, - {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":56,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":63,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":70,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":77,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":91,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":98,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":105,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":112,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":119,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":126,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":133,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":140,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, - {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":70,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2681632925},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":77,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":84,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":91,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":98,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":105,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":112,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":119,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":126,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":133,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":140,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":147,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":154,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":3581846399},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":70,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"publicKey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":77,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"LedgerContract", """{"id":-4,"updatecounter":0,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"currentHash","parameters":[],"returntype":"Hash256","offset":0,"safe":true},{"name":"currentIndex","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getBlock","parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","offset":14,"safe":true},{"name":"getTransaction","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":21,"safe":true},{"name":"getTransactionFromBlock","parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getTransactionHeight","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":35,"safe":true},{"name":"getTransactionSigners","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":42,"safe":true},{"name":"getTransactionVMState","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":49,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, - {"NeoToken", """{"id":-5,"updatecounter":0,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getAccountState","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","offset":14,"safe":true},{"name":"getAllCandidates","parameters":[],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"getCandidateVote","parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","offset":28,"safe":true},{"name":"getCandidates","parameters":[],"returntype":"Array","offset":35,"safe":true},{"name":"getCommittee","parameters":[],"returntype":"Array","offset":42,"safe":true},{"name":"getCommitteeAddress","parameters":[],"returntype":"Hash160","offset":49,"safe":true},{"name":"getGasPerBlock","parameters":[],"returntype":"Integer","offset":56,"safe":true},{"name":"getNextBlockValidators","parameters":[],"returntype":"Array","offset":63,"safe":true},{"name":"getRegisterPrice","parameters":[],"returntype":"Integer","offset":70,"safe":true},{"name":"registerCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":77,"safe":false},{"name":"setGasPerBlock","parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","offset":84,"safe":false},{"name":"setRegisterPrice","parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","offset":91,"safe":false},{"name":"symbol","parameters":[],"returntype":"String","offset":98,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":105,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":112,"safe":false},{"name":"unclaimedGas","parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","offset":119,"safe":true},{"name":"unregisterCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":126,"safe":false},{"name":"vote","parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","offset":133,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"NeoToken", """{"id":-5,"updatecounter":0,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","groups":[],"features":{},"supportedstandards":["NEP-17","NEP-27"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getAccountState","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","offset":14,"safe":true},{"name":"getAllCandidates","parameters":[],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"getCandidateVote","parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","offset":28,"safe":true},{"name":"getCandidates","parameters":[],"returntype":"Array","offset":35,"safe":true},{"name":"getCommittee","parameters":[],"returntype":"Array","offset":42,"safe":true},{"name":"getCommitteeAddress","parameters":[],"returntype":"Hash160","offset":49,"safe":true},{"name":"getGasPerBlock","parameters":[],"returntype":"Integer","offset":56,"safe":true},{"name":"getNextBlockValidators","parameters":[],"returntype":"Array","offset":63,"safe":true},{"name":"getRegisterPrice","parameters":[],"returntype":"Integer","offset":70,"safe":true},{"name":"onNEP17Payment","parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false},{"name":"registerCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":84,"safe":false},{"name":"setGasPerBlock","parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","offset":91,"safe":false},{"name":"setRegisterPrice","parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","offset":98,"safe":false},{"name":"symbol","parameters":[],"returntype":"String","offset":105,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":112,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":119,"safe":false},{"name":"unclaimedGas","parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","offset":126,"safe":true},{"name":"unregisterCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":133,"safe":false},{"name":"vote","parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","offset":140,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"GasToken", """{"id":-6,"updatecounter":0,"hash":"0xd2a4cff31913016155e38e474a2c06d08be276cf","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"PolicyContract", """{"id":-7,"updatecounter":0,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"blockAccount","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","offset":0,"safe":false},{"name":"getAttributeFee","parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"getExecFeeFactor","parameters":[],"returntype":"Integer","offset":14,"safe":true},{"name":"getFeePerByte","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"getStoragePrice","parameters":[],"returntype":"Integer","offset":28,"safe":true},{"name":"isBlocked","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","offset":35,"safe":true},{"name":"setAttributeFee","parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","offset":42,"safe":false},{"name":"setExecFeeFactor","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":49,"safe":false},{"name":"setFeePerByte","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":56,"safe":false},{"name":"setStoragePrice","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":63,"safe":false},{"name":"unblockAccount","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","offset":70,"safe":false}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, - {"RoleManagement", """{"id":-8,"updatecounter":0,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"designateAsRole","parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","offset":0,"safe":false},{"name":"getDesignatedByRole","parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","offset":7,"safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"RoleManagement", """{"id":-8,"updatecounter":0,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"designateAsRole","parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","offset":0,"safe":false},{"name":"getDesignatedByRole","parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","offset":7,"safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"},{"name":"Old","type":"Array"},{"name":"New","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"OracleContract", """{"id":-9,"updatecounter":0,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"finish","parameters":[],"returntype":"Void","offset":0,"safe":false},{"name":"getPrice","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"request","parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","offset":14,"safe":false},{"name":"setPrice","parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","offset":21,"safe":false},{"name":"verify","parameters":[],"returntype":"Boolean","offset":28,"safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, }; } @@ -71,7 +71,7 @@ public void TestActiveDeprecatedIn() string json = UT_ProtocolSettings.CreateHFSettings("\"HF_Cockatrice\": 20"); var file = Path.GetTempFileName(); File.WriteAllText(file, json); - ProtocolSettings settings = ProtocolSettings.Load(file, false); + ProtocolSettings settings = ProtocolSettings.Load(file); File.Delete(file); Assert.IsFalse(NativeContract.IsActive(new active() { ActiveIn = Hardfork.HF_Cockatrice, DeprecatedIn = null }, settings.IsHardforkEnabled, 1)); @@ -81,6 +81,24 @@ public void TestActiveDeprecatedIn() Assert.IsFalse(NativeContract.IsActive(new active() { ActiveIn = null, DeprecatedIn = Hardfork.HF_Cockatrice }, settings.IsHardforkEnabled, 20)); } + [TestMethod] + public void TestActiveDeprecatedInRoleManagement() + { + string json = UT_ProtocolSettings.CreateHFSettings("\"HF_Echidna\": 20"); + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file); + File.Delete(file); + + var before = NativeContract.RoleManagement.GetContractState(settings.IsHardforkEnabled, 19); + var after = NativeContract.RoleManagement.GetContractState(settings.IsHardforkEnabled, 20); + + Assert.AreEqual(2, before.Manifest.Abi.Events[0].Parameters.Length); + Assert.AreEqual(1, before.Manifest.Abi.Events.Length); + Assert.AreEqual(4, after.Manifest.Abi.Events[0].Parameters.Length); + Assert.AreEqual(1, after.Manifest.Abi.Events.Length); + } + [TestMethod] public void TestGetContract() { @@ -90,11 +108,11 @@ public void TestGetContract() [TestMethod] public void TestIsInitializeBlock() { - string json = UT_ProtocolSettings.CreateHFSettings("\"HF_Cockatrice\": 20"); + string json = UT_ProtocolSettings.CreateHFSettings("\"HF_Cockatrice\": 20,\n\"HF_Domovoi\": 30,\n\"HF_Echidna\": 40"); var file = Path.GetTempFileName(); File.WriteAllText(file, json); - ProtocolSettings settings = ProtocolSettings.Load(file, false); + ProtocolSettings settings = ProtocolSettings.Load(file); File.Delete(file); Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 0, out var hf)); diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 1b8304dffe..ef3481836d 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -22,8 +22,11 @@ using Neo.VM; using Neo.Wallets; using System; +using System.IO; using System.Linq; using System.Numerics; +using System.Security.Principal; +using System.Text; using static Neo.SmartContract.Native.NeoToken; namespace Neo.UnitTests.SmartContract.Native @@ -54,6 +57,48 @@ public void TestSetup() [TestMethod] public void Check_Decimals() => NativeContract.NEO.Decimals(_snapshotCache).Should().Be(0); + [TestMethod] + public void Test_HF_EchidnaStates() + { + string json = UT_ProtocolSettings.CreateHFSettings("\"HF_Echidna\": 10"); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + var settings = ProtocolSettings.Load(stream); + + var clonedCache = _snapshotCache.CloneCache(); + var persistingBlock = new Block { Header = new Header() }; + + foreach (var method in new string[] { "vote", "registerCandidate", "unregisterCandidate" }) + { + // Test WITHOUT HF_Echidna + + persistingBlock.Header.Index = 9; + + using (var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), clonedCache, persistingBlock, settings: settings)) + { + var methods = NativeContract.NEO.GetContractMethods(engine); + var entries = methods.Values.Where(u => u.Name == method).ToArray(); + + Assert.AreEqual(entries.Length, 1); + Assert.AreEqual(entries[0].RequiredCallFlags, CallFlags.States); + } + + // Test WITH HF_Echidna + + persistingBlock.Header.Index = 10; + + using (var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), clonedCache, persistingBlock, settings: settings)) + { + var methods = NativeContract.NEO.GetContractMethods(engine); + var entries = methods.Values.Where(u => u.Name == method).ToArray(); + + Assert.AreEqual(entries.Length, 1); + Assert.AreEqual(entries[0].RequiredCallFlags, CallFlags.States | CallFlags.AllowNotify); + } + } + } + [TestMethod] public void Check_Vote() { @@ -265,6 +310,40 @@ public void Check_RegisterValidator() Assert.AreEqual(2, members.Count()); } + [TestMethod] + public void Check_RegisterValidatorViaNEP27() + { + var clonedCache = _snapshotCache.CloneCache(); + var point = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var pointData = point.EncodePoint(true); + + // Send some NEO, shouldn't be accepted + var ret = Check_RegisterValidatorViaNEP27(clonedCache, point, _persistingBlock, true, pointData, 1000_0000_0000); + ret.State.Should().BeFalse(); + + // Send improper amount of GAS, shouldn't be accepted. + ret = Check_RegisterValidatorViaNEP27(clonedCache, point, _persistingBlock, false, pointData, 1000_0000_0001); + ret.State.Should().BeFalse(); + + // Broken witness. + var badPoint = ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", ECCurve.Secp256r1); + ret = Check_RegisterValidatorViaNEP27(clonedCache, point, _persistingBlock, false, badPoint.EncodePoint(true), 1000_0000_0000); + ret.State.Should().BeFalse(); + + // Successful case. + ret = Check_RegisterValidatorViaNEP27(clonedCache, point, _persistingBlock, false, pointData, 1000_0000_0000); + ret.State.Should().BeTrue(); + ret.Result.Should().BeTrue(); + + // Check GetRegisteredValidators + var members = NativeContract.NEO.GetCandidatesInternal(clonedCache); + Assert.AreEqual(1, members.Count()); + Assert.AreEqual(point, members.First().PublicKey); + + // No GAS should be left on the NEO account. + Assert.AreEqual(0, NativeContract.GAS.BalanceOf(clonedCache, NativeContract.NEO.Hash)); + } + [TestMethod] public void Check_UnregisterCandidate() { @@ -1054,6 +1133,36 @@ internal static (bool State, bool Result) Check_RegisterValidator(DataCache clon return (true, result.GetBoolean()); } + internal static (bool State, bool Result) Check_RegisterValidatorViaNEP27(DataCache clonedCache, ECPoint pubkey, Block persistingBlock, bool passNEO, byte[] data, BigInteger amount) + { + var keyScriptHash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); + var contractID = passNEO ? NativeContract.NEO.Id : NativeContract.GAS.Id; + var storageKey = new KeyBuilder(contractID, 20).Add(keyScriptHash); // 20 is Prefix_Account + + if (passNEO) + clonedCache.Add(storageKey, new StorageItem(new NeoAccountState { Balance = amount })); + else + clonedCache.Add(storageKey, new StorageItem(new AccountState { Balance = amount })); + + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(keyScriptHash), clonedCache, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1_0000_0000); + + using var script = new ScriptBuilder(); + script.EmitDynamicCall(passNEO ? NativeContract.NEO.Hash : NativeContract.GAS.Hash, "transfer", keyScriptHash, NativeContract.NEO.Hash, amount, data); + engine.LoadScript(script.ToArray()); + + var execRes = engine.Execute(); + clonedCache.Delete(storageKey); // Clean up for subsequent invocations. + + if (execRes == VMState.FAULT) + return (false, false); + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + + return (true, result.GetBoolean()); + } + internal static ECPoint[] Check_GetCommittee(DataCache clonedCache, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, clonedCache, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 5766d1be38..4878e9cc8a 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -406,5 +406,25 @@ public void TestRuntime_Deserialize() Assert.AreEqual(engine.ResultStack.Pop().GetInteger(), 100); Assert.AreEqual(engine.ResultStack.Pop().GetString(), "test"); } + + [TestMethod] + public void TestBase64Url() + { + var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + using (var script = new ScriptBuilder()) + { + // Test encoding + script.EmitDynamicCall(NativeContract.StdLib.Hash, "base64UrlEncode", "Subject=test@example.com&Issuer=https://example.com"); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "base64UrlDecode", "U3ViamVjdD10ZXN0QGV4YW1wbGUuY29tJklzc3Vlcj1odHRwczovL2V4YW1wbGUuY29t"); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestBlockchain.TheNeoSystem.Settings); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + Assert.AreEqual("Subject=test@example.com&Issuer=https://example.com", engine.ResultStack.Pop()); + Assert.AreEqual("U3ViamVjdD10ZXN0QGV4YW1wbGUuY29tJklzc3Vlcj1odHRwczovL2V4YW1wbGUuY29t", engine.ResultStack.Pop().GetString()); + } + } } } diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs index 40828413c6..0ed01c914b 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Extensions; +using Neo.Persistence; using Neo.SmartContract; using Neo.VM; using System.Linq; @@ -20,11 +21,20 @@ namespace Neo.UnitTests.SmartContract { public partial class UT_ApplicationEngine { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void TestCreateStandardAccount() { + var snapshot = _snapshotCache.CloneCache(); var settings = TestProtocolSettings.Default; - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestProtocolSettings.Default, gas: 1100_00000000); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestProtocolSettings.Default, gas: 1100_00000000); using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_CreateStandardAccount, settings.StandbyCommittee[0].EncodePoint(true)); @@ -39,8 +49,9 @@ public void TestCreateStandardAccount() [TestMethod] public void TestCreateStandardMultisigAccount() { + var snapshot = _snapshotCache.CloneCache(); var settings = TestProtocolSettings.Default; - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_CreateMultisigAccount, new object[] diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs index 7f66bb958a..e0447e1c12 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs @@ -21,9 +21,12 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_ApplicationEngineProvider { + private DataCache _snapshotCache; + [TestInitialize] - public void TestInitialize() + public void TestSetup() { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); ApplicationEngine.Provider = null; } @@ -37,15 +40,17 @@ public void TestCleanup() public void TestSetAppEngineProvider() { ApplicationEngine.Provider = new TestProvider(); + var snapshot = _snapshotCache.CloneCache(); - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); (appEngine is TestEngine).Should().BeTrue(); } [TestMethod] public void TestDefaultAppEngineProvider() { - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); + var snapshot = _snapshotCache.CloneCache(); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); (appEngine is ApplicationEngine).Should().BeTrue(); } diff --git a/tests/Neo.UnitTests/SmartContract/UT_Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs index 21994b07d9..273ea9faed 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Extensions; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -26,6 +27,14 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_Contract { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void TestGetScriptHash() { @@ -158,6 +167,7 @@ public void TestCreateSignatureRedeemScript() [TestMethod] public void TestSignatureRedeemScriptFee() { + var snapshot = _snapshotCache.CloneCache(); byte[] privateKey = new byte[32]; RandomNumberGenerator rng = RandomNumberGenerator.Create(); rng.GetBytes(privateKey); @@ -167,7 +177,8 @@ public void TestSignatureRedeemScriptFee() var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, + new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) { engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); @@ -178,6 +189,7 @@ public void TestSignatureRedeemScriptFee() [TestMethod] public void TestCreateMultiSigRedeemScriptFee() { + var snapshot = _snapshotCache.CloneCache(); byte[] privateKey1 = new byte[32]; RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); rng1.GetBytes(privateKey1); @@ -195,7 +207,8 @@ public void TestCreateMultiSigRedeemScriptFee() long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePriceTable[(byte)OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePriceTable[(byte)OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice * 2); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, + new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) { engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); diff --git a/tests/Neo.UnitTests/SmartContract/UT_Helper.cs b/tests/Neo.UnitTests/SmartContract/UT_Helper.cs index 27cb009631..fc8caa696b 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Helper.cs @@ -12,6 +12,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -27,10 +28,12 @@ namespace Neo.UnitTests.SmartContract public class UT_Helper { private KeyPair _key; + private DataCache _snapshotCache; [TestInitialize] - public void Init() + public void TestSetup() { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); var pk = new byte[32]; new Random().NextBytes(pk); _key = new KeyPair(pk); @@ -118,6 +121,7 @@ public void TestIsSignatureContract_WrongCurve() [TestMethod] public void TestSignatureContractCost() { + var snapshot = _snapshotCache.CloneCache(); var contract = Contract.CreateSignatureContract(_key.PublicKey); var tx = TestUtils.CreateRandomHashTransaction(); @@ -127,7 +131,7 @@ public void TestSignatureContractCost() invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, TestProtocolSettings.Default.Network)); tx.Witnesses = new Witness[] { new Witness() { InvocationScript = invocationScript.ToArray(), VerificationScript = contract.Script } }; - using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, TestProtocolSettings.Default); + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, TestProtocolSettings.Default); engine.LoadScript(contract.Script); engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -139,6 +143,7 @@ public void TestSignatureContractCost() [TestMethod] public void TestMultiSignatureContractCost() { + var snapshot = _snapshotCache.CloneCache(); var contract = Contract.CreateMultiSigContract(1, new ECPoint[] { _key.PublicKey }); var tx = TestUtils.CreateRandomHashTransaction(); @@ -147,7 +152,7 @@ public void TestMultiSignatureContractCost() using ScriptBuilder invocationScript = new(); invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, TestProtocolSettings.Default.Network)); - using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, TestProtocolSettings.Default); + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, TestProtocolSettings.Default); engine.LoadScript(contract.Script); engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); Assert.AreEqual(VMState.HALT, engine.Execute()); diff --git a/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs index 6f9cf4cbe0..d70828bc4d 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Persistence; using Neo.SmartContract; using Neo.UnitTests.Extensions; using Neo.VM; @@ -20,12 +21,21 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_InteropPrices { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void ApplicationEngineFixedPrices() { + var snapshot = _snapshotCache.CloneCache(); // System.Runtime.CheckWitness: f827ec8c (price is 200) byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); ApplicationEngine.System_Runtime_CheckWitness.FixedPrice.Should().Be(0_00001024L); @@ -33,7 +43,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.GetContext: 9bf667ce (price is 1) byte[] SyscallSystemStorageGetContextHash = new byte[] { 0x68, 0x9b, 0xf6, 0x67, 0xce }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); ApplicationEngine.System_Storage_GetContext.FixedPrice.Should().Be(0_00000016L); @@ -41,7 +51,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.Get: 925de831 (price is 100) byte[] SyscallSystemStorageGetHash = new byte[] { 0x68, 0x92, 0x5d, 0xe8, 0x31 }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 0)) { ae.LoadScript(SyscallSystemStorageGetHash); ApplicationEngine.System_Storage_Get.FixedPrice.Should().Be(32768L); @@ -54,6 +64,7 @@ public void ApplicationEngineFixedPrices() [TestMethod] public void ApplicationEngineRegularPut() { + var snapshot = _snapshotCache.CloneCache(); var key = new byte[] { (byte)OpCode.PUSH1 }; var value = new byte[] { (byte)OpCode.PUSH1 }; @@ -64,11 +75,10 @@ public void ApplicationEngineRegularPut() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(System.Array.Empty()); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); - snapshotCache.Add(skey, sItem); - snapshotCache.AddContract(script.ToScriptHash(), contractState); + snapshot.Add(skey, sItem); + snapshot.AddContract(script.ToScriptHash(), contractState); - using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot); Debugger debugger = new(ae); ae.LoadScript(script); debugger.StepInto(); @@ -85,6 +95,7 @@ public void ApplicationEngineRegularPut() [TestMethod] public void ApplicationEngineReusedStorage_FullReuse() { + var snapshot = _snapshotCache.CloneCache(); var key = new byte[] { (byte)OpCode.PUSH1 }; var value = new byte[] { (byte)OpCode.PUSH1 }; @@ -95,11 +106,10 @@ public void ApplicationEngineReusedStorage_FullReuse() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(value); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); - snapshotCache.Add(skey, sItem); - snapshotCache.AddContract(script.ToScriptHash(), contractState); + snapshot.Add(skey, sItem); + snapshot.AddContract(script.ToScriptHash(), contractState); - using ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + using ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); Debugger debugger = new(applicationEngine); applicationEngine.LoadScript(script); debugger.StepInto(); @@ -117,6 +127,7 @@ public void ApplicationEngineReusedStorage_FullReuse() [TestMethod] public void ApplicationEngineReusedStorage_PartialReuse() { + var snapshot = _snapshotCache.CloneCache(); var key = new byte[] { (byte)OpCode.PUSH1 }; var oldValue = new byte[] { (byte)OpCode.PUSH1 }; var value = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH1 }; @@ -128,11 +139,10 @@ public void ApplicationEngineReusedStorage_PartialReuse() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(oldValue); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); - snapshotCache.Add(skey, sItem); - snapshotCache.AddContract(script.ToScriptHash(), contractState); + snapshot.Add(skey, sItem); + snapshot.AddContract(script.ToScriptHash(), contractState); - using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot); Debugger debugger = new(ae); ae.LoadScript(script); debugger.StepInto(); @@ -151,6 +161,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() [TestMethod] public void ApplicationEngineReusedStorage_PartialReuseTwice() { + var snapshot = _snapshotCache.CloneCache(); var key = new byte[] { (byte)OpCode.PUSH1 }; var oldValue = new byte[] { (byte)OpCode.PUSH1 }; var value = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH1 }; @@ -162,11 +173,10 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(oldValue); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); - snapshotCache.Add(skey, sItem); - snapshotCache.AddContract(script.ToScriptHash(), contractState); + snapshot.Add(skey, sItem); + snapshot.AddContract(script.ToScriptHash(), contractState); - using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + using ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot); Debugger debugger = new(ae); ae.LoadScript(script); debugger.StepInto(); //push value diff --git a/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 755c739760..7c671359ad 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -244,7 +244,7 @@ public void TestContract_Update_Invalid() [TestMethod] public void TestStorage_Find() { - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + var snapshot = _snapshotCache.CloneCache(); var state = TestUtils.GetContract(); var storageItem = new StorageItem @@ -256,9 +256,9 @@ public void TestStorage_Find() Id = state.Id, Key = new byte[] { 0x01 } }; - snapshotCache.AddContract(state.Hash, state); - snapshotCache.Add(storageKey, storageItem); - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + snapshot.AddContract(state.Hash, state); + snapshot.Add(storageKey, storageItem); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); var iterator = engine.Find(new StorageContext diff --git a/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs index 0189e77549..6cee5c7edf 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs @@ -19,6 +19,7 @@ using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; @@ -35,6 +36,14 @@ namespace Neo.UnitTests.SmartContract [TestClass] public partial class UT_InteropService : TestKit { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void Runtime_GetNotifications_Test() { @@ -365,7 +374,7 @@ public void TestRuntime_Log() public void TestRuntime_GetTime() { Block block = new() { Header = new Header() }; - var engine = GetEngine(true, true, hasBlock: true); + var engine = GetEngine(true, hasBlock: true); engine.GetTime().Should().Be(block.Timestamp); } @@ -391,7 +400,7 @@ public void TestRuntime_GetCurrentSigners_SysCall() // Null - using var engineA = GetEngine(hasSnapshot: true, addScript: false, hasContainer: false); + using var engineA = GetEngine(addScript: false, hasContainer: false); engineA.LoadScript(script.ToArray()); engineA.Execute(); @@ -402,7 +411,7 @@ public void TestRuntime_GetCurrentSigners_SysCall() // Not null - using var engineB = GetEngine(hasSnapshot: true, addScript: false, hasContainer: true); + using var engineB = GetEngine(addScript: false, hasContainer: true); engineB.LoadScript(script.ToArray()); engineB.Execute(); @@ -472,7 +481,7 @@ public void TestBlockchain_GetTransaction() [TestMethod] public void TestBlockchain_GetTransactionHeight() { - var engine = GetEngine(hasSnapshot: true, addScript: false); + var engine = GetEngine(addScript: false); var state = new TransactionState() { BlockIndex = 0, @@ -501,10 +510,9 @@ public void TestBlockchain_GetContract() 0x01, 0x01, 0x01, 0x01, 0x01 }; NativeContract.ContractManagement.GetContract(engine.SnapshotCache, new UInt160(data1)).Should().BeNull(); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); var state = TestUtils.GetContract(); - snapshotCache.AddContract(state.Hash, state); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + engine.SnapshotCache.AddContract(state.Hash, state); + engine = ApplicationEngine.Create(TriggerType.Application, null, engine.SnapshotCache); engine.LoadScript(new byte[] { 0x01 }); NativeContract.ContractManagement.GetContract(engine.SnapshotCache, state.Hash).Hash.Should().Be(state.Hash); } @@ -533,15 +541,14 @@ public void TestBlockchain_ListContracts() var list = NativeContract.ContractManagement.ListContracts(engine.SnapshotCache); list.ForEach(p => p.Id.Should().BeLessThan(0)); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); var state = TestUtils.GetContract(); - snapshotCache.AddContract(state.Hash, state); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + engine.SnapshotCache.AddContract(state.Hash, state); + engine = ApplicationEngine.Create(TriggerType.Application, null, engine.SnapshotCache); engine.LoadScript(new byte[] { 0x01 }); NativeContract.ContractManagement.GetContract(engine.SnapshotCache, state.Hash).Hash.Should().Be(state.Hash); var list2 = NativeContract.ContractManagement.ListContracts(engine.SnapshotCache); - list2.Count().Should().Be(list.Count() + 1); + list2.Count().Should().Be(list.Count()); } [TestMethod] @@ -655,7 +662,6 @@ public void TestStorage_Put() public void TestStorage_Delete() { var engine = GetEngine(false, true); - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); var state = TestUtils.GetContract(); var storageKey = new StorageKey { @@ -666,9 +672,9 @@ public void TestStorage_Delete() { Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; - snapshotCache.AddContract(state.Hash, state); - snapshotCache.Add(storageKey, storageItem); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + engine.SnapshotCache.AddContract(state.Hash, state); + engine.SnapshotCache.Add(storageKey, storageItem); + engine = ApplicationEngine.Create(TriggerType.Application, null, engine.SnapshotCache); engine.LoadScript(new byte[] { 0x01 }); var key = new byte[] { 0x01 }; var storageContext = new StorageContext @@ -767,12 +773,12 @@ public static void LogEvent(object sender, LogEventArgs args) tx.Script = new byte[] { 0x01, 0x02, 0x03 }; } - private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool hasBlock = false, bool addScript = true, long gas = 20_00000000) + private ApplicationEngine GetEngine(bool hasContainer = false, bool hasBlock = false, bool addScript = true, long gas = 20_00000000) { + var snapshot = _snapshotCache.CloneCache(); var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; - var snapshotCache = hasSnapshot ? TestBlockchain.GetTestSnapshotCache() : null; var block = hasBlock ? new Block { Header = new Header() } : null; - var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, block, TestBlockchain.TheNeoSystem.Settings, gas: gas); + var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, TestBlockchain.TheNeoSystem.Settings, gas: gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); return engine; } diff --git a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 45f1a9e58d..244cae2660 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Json; +using Neo.Persistence; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -24,6 +25,14 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_JsonSerializer { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void JsonTest_WrongJson() { @@ -193,7 +202,8 @@ public void JsonTest_Object() [TestMethod] public void Deserialize_WrongJson() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); Assert.ThrowsException(() => JsonSerializer.Deserialize(engine, JObject.Parse("x"), ExecutionEngineLimits.Default)); } @@ -228,7 +238,8 @@ public void Serialize_Null() [TestMethod] public void Deserialize_EmptyObject() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); var items = JsonSerializer.Deserialize(engine, JObject.Parse("{}"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(Map)); @@ -247,7 +258,8 @@ public void Serialize_EmptyArray() [TestMethod] public void Deserialize_EmptyArray() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); var items = JsonSerializer.Deserialize(engine, JObject.Parse("[]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); @@ -272,7 +284,8 @@ public void Serialize_Map_Test() [TestMethod] public void Deserialize_Map_Test() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default); var items = JsonSerializer.Deserialize(engine, JObject.Parse("{\"test1\":123,\"test2\":321}"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(Map)); @@ -302,7 +315,8 @@ public void Serialize_Array_Bool_Str_Num() [TestMethod] public void Deserialize_Array_Bool_Str_Num() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default); var items = JsonSerializer.Deserialize(engine, JObject.Parse("[true,\"test\",123,9.05E+28]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); @@ -333,7 +347,8 @@ public void Serialize_Array_OfArray() [TestMethod] public void Deserialize_Array_OfArray() { - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default); + var snapshot = _snapshotCache.CloneCache(); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default); var items = JsonSerializer.Deserialize(engine, JObject.Parse("[[true,\"test1\",123],[true,\"test2\",321]]"), ExecutionEngineLimits.Default); Assert.IsInstanceOfType(items, typeof(VM.Types.Array)); diff --git a/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs index 1d24663162..5896718c6c 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -21,6 +22,14 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_NotifyEventArgs { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void TestGetScriptContainer() { @@ -30,11 +39,11 @@ public void TestGetScriptContainer() args.ScriptContainer.Should().Be(container); } - [TestMethod] public void TestIssue3300() // https://github.com/neo-project/neo/issues/3300 { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestProtocolSettings.Default, gas: 1100_00000000); + var snapshot = _snapshotCache.CloneCache(); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestProtocolSettings.Default, gas: 1100_00000000); using (var script = new ScriptBuilder()) { // Build call script calling disallowed method. diff --git a/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs index 79d7c1d114..915fafad16 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -15,6 +15,7 @@ using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; @@ -28,6 +29,14 @@ namespace Neo.UnitTests.SmartContract [TestClass] public partial class UT_Syscalls : TestKit { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void System_Blockchain_GetBlock() { @@ -63,14 +72,14 @@ public void System_Blockchain_GetBlock() Hashes = new[] { tx.Hash } }; - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + var snapshot = _snapshotCache.CloneCache(); using ScriptBuilder script = new(); script.EmitDynamicCall(NativeContract.Ledger.Hash, "getBlock", block.Hash.ToArray()); // Without block - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestBlockchain.TheNeoSystem.Settings); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -82,18 +91,18 @@ public void System_Blockchain_GetBlock() const byte Prefix_Transaction = 11; const byte Prefix_CurrentBlock = 12; - TestUtils.BlocksAdd(snapshotCache, block.Hash, block); + TestUtils.BlocksAdd(snapshot, block.Hash, block); - var height = snapshotCache[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); + var height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); height.Index = block.Index + TestProtocolSettings.Default.MaxTraceableBlocks; - snapshotCache.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Hash), new StorageItem(new TransactionState + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Hash), new StorageItem(new TransactionState { BlockIndex = block.Index, Transaction = tx })); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestBlockchain.TheNeoSystem.Settings); + engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -102,10 +111,10 @@ public void System_Blockchain_GetBlock() // With block - height = snapshotCache[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); + height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); height.Index = block.Index; - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestBlockchain.TheNeoSystem.Settings); + engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -118,13 +127,13 @@ public void System_Blockchain_GetBlock() [TestMethod] public void System_ExecutionEngine_GetScriptContainer() { - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + var snapshot = _snapshotCache.CloneCache(); using ScriptBuilder script = new(); script.EmitSysCall(ApplicationEngine.System_Runtime_GetScriptContainer); // Without tx - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.FAULT); @@ -154,7 +163,7 @@ public void System_ExecutionEngine_GetScriptContainer() Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, }; - engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache); + engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -167,7 +176,7 @@ public void System_ExecutionEngine_GetScriptContainer() [TestMethod] public void System_Runtime_GasLeft() { - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + var snapshot = _snapshotCache.CloneCache(); using (var script = new ScriptBuilder()) { @@ -182,7 +191,7 @@ public void System_Runtime_GasLeft() // Execute - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, gas: 100_000_000); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 100_000_000); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -203,7 +212,7 @@ public void System_Runtime_GasLeft() // Execute - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); // Check the results @@ -218,8 +227,8 @@ public void System_Runtime_GasLeft() [TestMethod] public void System_Runtime_GetInvocationCounter() { + var snapshot = _snapshotCache.CloneCache(); ContractState contractA, contractB, contractC; - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); // Create dummy contracts @@ -237,15 +246,15 @@ public void System_Runtime_GetInvocationCounter() // Init A,B,C contracts // First two drops is for drop method and arguments - snapshotCache.DeleteContract(contractA.Hash); - snapshotCache.DeleteContract(contractB.Hash); - snapshotCache.DeleteContract(contractC.Hash); + snapshot.DeleteContract(contractA.Hash); + snapshot.DeleteContract(contractB.Hash); + snapshot.DeleteContract(contractC.Hash); contractA.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); contractB.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); contractC.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); - snapshotCache.AddContract(contractA.Hash, contractA); - snapshotCache.AddContract(contractB.Hash, contractB); - snapshotCache.AddContract(contractC.Hash, contractC); + snapshot.AddContract(contractA.Hash, contractA); + snapshot.AddContract(contractB.Hash, contractB); + snapshot.AddContract(contractC.Hash, contractC); } // Call A,B,B,C @@ -259,7 +268,7 @@ public void System_Runtime_GetInvocationCounter() // Execute - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, null, ProtocolSettings.Default); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default); engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); diff --git a/tests/Neo.UnitTests/UT_ProtocolSettings.cs b/tests/Neo.UnitTests/UT_ProtocolSettings.cs index 8b17e46da0..5e902ea472 100644 --- a/tests/Neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/Neo.UnitTests/UT_ProtocolSettings.cs @@ -56,7 +56,7 @@ public void HardForkTestBAndNotA() var file = Path.GetTempFileName(); File.WriteAllText(file, json); - ProtocolSettings settings = ProtocolSettings.Load(file, false); + ProtocolSettings settings = ProtocolSettings.Load(file); File.Delete(file); settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); @@ -78,7 +78,7 @@ public void HardForkTestAAndNotB() var file = Path.GetTempFileName(); File.WriteAllText(file, json); - ProtocolSettings settings = ProtocolSettings.Load(file, false); + ProtocolSettings settings = ProtocolSettings.Load(file); File.Delete(file); settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); @@ -100,7 +100,7 @@ public void HardForkTestNone() var file = Path.GetTempFileName(); File.WriteAllText(file, json); - ProtocolSettings settings = ProtocolSettings.Load(file, false); + ProtocolSettings settings = ProtocolSettings.Load(file); File.Delete(file); settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0); @@ -120,7 +120,7 @@ public void HardForkTestAMoreThanB() string json = CreateHFSettings("\"HF_Aspidochelone\": 4120001, \"HF_Basilisk\": 4120000"); var file = Path.GetTempFileName(); File.WriteAllText(file, json); - Assert.ThrowsException(() => ProtocolSettings.Load(file, false)); + Assert.ThrowsException(() => ProtocolSettings.Load(file)); File.Delete(file); } @@ -316,7 +316,7 @@ public void TestTimePerBlockCalculation() [TestMethod] public void TestLoad() { - var loadedSetting = ProtocolSettings.Load("test.config.json", false); + var loadedSetting = ProtocolSettings.Load("test.config.json"); // Comparing all properties TestProtocolSettings.Default.Network.Should().Be(loadedSetting.Network); diff --git a/tests/Neo.UnitTests/VM/UT_Helper.cs b/tests/Neo.UnitTests/VM/UT_Helper.cs index ccbb42ffd1..ea0f2a4dd8 100644 --- a/tests/Neo.UnitTests/VM/UT_Helper.cs +++ b/tests/Neo.UnitTests/VM/UT_Helper.cs @@ -14,6 +14,7 @@ using Neo.Cryptography.ECC; using Neo.Extensions; using Neo.IO; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -31,6 +32,14 @@ namespace Neo.UnitTests.VMT [TestClass] public class UT_Helper { + private DataCache _snapshotCache; + + [TestInitialize] + public void TestSetup() + { + _snapshotCache = TestBlockchain.GetTestSnapshotCache(); + } + [TestMethod] public void TestEmit() { @@ -85,11 +94,12 @@ public void TestEmitAppCall1() [TestMethod] public void TestEmitArray() { + var snapshot = _snapshotCache.CloneCache(); var expected = new BigInteger[] { 1, 2, 3 }; var sb = new ScriptBuilder(); sb.CreateArray(expected); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(sb.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -99,7 +109,7 @@ public void TestEmitArray() sb = new ScriptBuilder(); sb.CreateArray(expected); - using var engine2 = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine2 = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine2.LoadScript(sb.ToArray()); Assert.AreEqual(VMState.HALT, engine2.Execute()); @@ -109,11 +119,12 @@ public void TestEmitArray() [TestMethod] public void TestEmitStruct() { + var snapshot = _snapshotCache.CloneCache(); var expected = new BigInteger[] { 1, 2, 3 }; var sb = new ScriptBuilder(); sb.CreateStruct(expected); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(sb.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -123,7 +134,7 @@ public void TestEmitStruct() sb = new ScriptBuilder(); sb.CreateStruct(expected); - using var engine2 = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine2 = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine2.LoadScript(sb.ToArray()); Assert.AreEqual(VMState.HALT, engine2.Execute()); @@ -133,11 +144,12 @@ public void TestEmitStruct() [TestMethod] public void TestEmitMap() { + var snapshot = _snapshotCache.CloneCache(); var expected = new Dictionary() { { 1, 2 }, { 3, 4 } }; var sb = new ScriptBuilder(); sb.CreateMap(expected); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(sb.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json index 7b78d7e053..13bf8b9b59 100644 --- a/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Splice/SUBSTR.json @@ -473,6 +473,50 @@ } } ] + }, + { + "name": "Count Exceed Range Test", + "script": [ + "PUSHDATA1", + "0x0a", + "0x00010203040506070809", + "PUSH2", + "PUSHINT32", + "0x7FFFFFFF", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] + }, + { + "name": "Index Exceed Range Test", + "script": [ + "PUSHDATA1", + "0x0a", + "0x00010203040506070809", + "PUSHINT32", + "0x7FFFFFFF", + "PUSH2", + "SUBSTR" + ], + "steps": [ + { + "actions": [ + "execute" + ], + "result": { + "state": "FAULT" + } + } + ] } ] }