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"
+ }
+ }
+ ]
}
]
}