diff --git a/src/Neo/Hardfork.cs b/src/Neo/Hardfork.cs
index a5decc1d0c..9ef6a63c1c 100644
--- a/src/Neo/Hardfork.cs
+++ b/src/Neo/Hardfork.cs
@@ -14,6 +14,7 @@ namespace Neo
public enum Hardfork : byte
{
HF_Aspidochelone,
- HF_Basilisk
+ HF_Basilisk,
+ HF_Cockatrice
}
}
diff --git a/src/Neo/NeoSystem.cs b/src/Neo/NeoSystem.cs
index d299f8de12..1303770a3f 100644
--- a/src/Neo/NeoSystem.cs
+++ b/src/Neo/NeoSystem.cs
@@ -117,7 +117,8 @@ static NeoSystem()
/// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used.
/// The path of the storage. If is the default in-memory storage engine, this parameter is ignored.
public NeoSystem(ProtocolSettings settings, string? storageProvider = null, string? storagePath = null) :
- this(settings, StoreFactory.GetStoreProvider(storageProvider ?? nameof(MemoryStore)), storagePath)
+ this(settings, StoreFactory.GetStoreProvider(storageProvider ?? nameof(MemoryStore))
+ ?? throw new ArgumentException($"Can't find the storage provider {storageProvider}", nameof(storageProvider)), storagePath)
{
}
diff --git a/src/Neo/Persistence/DataCache.cs b/src/Neo/Persistence/DataCache.cs
index 6a1a5236bc..86fbd69961 100644
--- a/src/Neo/Persistence/DataCache.cs
+++ b/src/Neo/Persistence/DataCache.cs
@@ -96,7 +96,7 @@ public void Add(StorageKey key, StorageItem value)
{
TrackState.Deleted => TrackState.Changed,
TrackState.NotFound => TrackState.Added,
- _ => throw new ArgumentException()
+ _ => throw new ArgumentException($"The element currently has state {trackable.State}")
};
}
else
diff --git a/src/Neo/ProtocolSettings.cs b/src/Neo/ProtocolSettings.cs
index defbab9994..1b7367a6a2 100644
--- a/src/Neo/ProtocolSettings.cs
+++ b/src/Neo/ProtocolSettings.cs
@@ -24,6 +24,8 @@ namespace Neo
///
public record ProtocolSettings
{
+ private static readonly IList AllHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToArray();
+
///
/// The magic number of the NEO network.
///
@@ -115,7 +117,7 @@ public record ProtocolSettings
MemoryPoolMaxTransactions = 50_000,
MaxTraceableBlocks = 2_102_400,
InitialGasDistribution = 52_000_000_00000000,
- Hardforks = ImmutableDictionary.Empty
+ Hardforks = EnsureOmmitedHardforks(new Dictionary()).ToImmutableDictionary()
};
public static ProtocolSettings? Custom { get; set; }
@@ -159,17 +161,39 @@ public static ProtocolSettings Load(IConfigurationSection section)
MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks),
InitialGasDistribution = section.GetValue("InitialGasDistribution", Default.InitialGasDistribution),
Hardforks = section.GetSection("Hardforks").Exists()
- ? section.GetSection("Hardforks").GetChildren().ToImmutableDictionary(p => Enum.Parse(p.Key), p => uint.Parse(p.Value))
+ ? EnsureOmmitedHardforks(section.GetSection("Hardforks").GetChildren().ToDictionary(p => Enum.Parse(p.Key, true), p => uint.Parse(p.Value))).ToImmutableDictionary()
: Default.Hardforks
};
}
+ ///
+ /// Explicitly set the height of all old omitted hardforks to 0 for proper IsHardforkEnabled behaviour.
+ ///
+ /// HardForks
+ /// Processed hardfork configuration
+ private static Dictionary EnsureOmmitedHardforks(Dictionary hardForks)
+ {
+ foreach (Hardfork hf in AllHardforks)
+ {
+ if (!hardForks.ContainsKey(hf))
+ {
+ hardForks[hf] = 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return hardForks;
+ }
+
private static void CheckingHardfork(ProtocolSettings settings)
{
var allHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToList();
// Check for continuity in configured hardforks
var sortedHardforks = settings.Hardforks.Keys
- .OrderBy(h => allHardforks.IndexOf(h))
+ .OrderBy(allHardforks.IndexOf)
.ToList();
for (int i = 0; i < sortedHardforks.Count - 1; i++)
@@ -179,7 +203,7 @@ private static void CheckingHardfork(ProtocolSettings settings)
// If they aren't consecutive, return false.
if (nextIndex - currentIndex > 1)
- throw new Exception("Hardfork configuration is not continuous.");
+ throw new ArgumentException("Hardfork configuration is not continuous.");
}
// Check that block numbers are not higher in earlier hardforks than in later ones
for (int i = 0; i < sortedHardforks.Count - 1; i++)
@@ -187,9 +211,27 @@ private static void CheckingHardfork(ProtocolSettings settings)
if (settings.Hardforks[sortedHardforks[i]] > settings.Hardforks[sortedHardforks[i + 1]])
{
// This means the block number for the current hardfork is greater than the next one, which should not be allowed.
- throw new Exception($"The Hardfork configuration for {sortedHardforks[i]} is greater than for {sortedHardforks[i + 1]}");
+ throw new ArgumentException($"The Hardfork configuration for {sortedHardforks[i]} is greater than for {sortedHardforks[i + 1]}");
}
}
}
+
+ ///
+ /// Check if the Hardfork is Enabled
+ ///
+ /// Hardfork
+ /// Block index
+ /// True if enabled
+ public bool IsHardforkEnabled(Hardfork hardfork, uint index)
+ {
+ if (Hardforks.TryGetValue(hardfork, out uint height))
+ {
+ // If the hardfork has a specific height in the configuration, check the block height.
+ return index >= height;
+ }
+
+ // If the hardfork isn't specified in the configuration, return false.
+ return false;
+ }
}
}
diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs
index 1331ff2608..63cbde9960 100644
--- a/src/Neo/SmartContract/ApplicationEngine.cs
+++ b/src/Neo/SmartContract/ApplicationEngine.cs
@@ -49,7 +49,6 @@ public partial class ApplicationEngine : ExecutionEngine
///
public static event EventHandler Log;
- private static readonly IList AllHardforks = Enum.GetValues(typeof(Hardfork)).Cast().ToArray();
private static Dictionary services;
private readonly long gas_amount;
private Dictionary states;
@@ -647,6 +646,25 @@ public T GetState()
return (T)state;
}
+ public T GetState(Func factory)
+ {
+ if (states is null)
+ {
+ T state = factory();
+ SetState(state);
+ return state;
+ }
+ else
+ {
+ if (!states.TryGetValue(typeof(T), out object state))
+ {
+ state = factory();
+ SetState(state);
+ }
+ return (T)state;
+ }
+ }
+
public void SetState(T state)
{
states ??= new Dictionary();
@@ -655,28 +673,11 @@ public void SetState(T state)
public bool IsHardforkEnabled(Hardfork hardfork)
{
- // Return true if there's no specific configuration or PersistingBlock is null
- if (PersistingBlock is null || ProtocolSettings.Hardforks.Count == 0)
- return true;
-
- // If the hardfork isn't specified in the configuration, check if it's a new one.
- if (!ProtocolSettings.Hardforks.ContainsKey(hardfork))
- {
- int currentHardforkIndex = AllHardforks.IndexOf(hardfork);
- int lastConfiguredHardforkIndex = AllHardforks.IndexOf(ProtocolSettings.Hardforks.Keys.Last());
+ // Return true if PersistingBlock is null and Hardfork is enabled
+ if (PersistingBlock is null)
+ return ProtocolSettings.Hardforks.ContainsKey(hardfork);
- // If it's a newer hardfork compared to the ones in the configuration, disable it.
- if (currentHardforkIndex > lastConfiguredHardforkIndex)
- return false;
- }
-
- if (ProtocolSettings.Hardforks.TryGetValue(hardfork, out uint height))
- {
- // If the hardfork has a specific height in the configuration, check the block height.
- return PersistingBlock.Index >= height;
- }
- // If no specific conditions are met, return true.
- return true;
+ return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index);
}
}
}
diff --git a/src/Neo/SmartContract/Native/ContractEventAttribute.cs b/src/Neo/SmartContract/Native/ContractEventAttribute.cs
new file mode 100644
index 0000000000..656ecef725
--- /dev/null
+++ b/src/Neo/SmartContract/Native/ContractEventAttribute.cs
@@ -0,0 +1,165 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// ContractEventAttribute.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.SmartContract.Manifest;
+using System;
+using System.Diagnostics;
+
+namespace Neo.SmartContract.Native
+{
+ [DebuggerDisplay("{Descriptor.Name}")]
+ [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)]
+ internal class ContractEventAttribute : Attribute
+ {
+ public int Order { get; init; }
+ public ContractEventDescriptor Descriptor { get; set; }
+ public Hardfork? ActiveIn { get; init; } = null;
+
+ public ContractEventAttribute(Hardfork activeIn, int order, string name,
+ string arg1Name, ContractParameterType arg1Value) : this(order, name, arg1Name, arg1Value)
+ {
+ ActiveIn = activeIn;
+ }
+
+ public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value)
+ {
+ Order = order;
+ Descriptor = new ContractEventDescriptor()
+ {
+ Name = name,
+ Parameters = new ContractParameterDefinition[]
+ {
+ new ContractParameterDefinition()
+ {
+ Name = arg1Name,
+ Type = arg1Value
+ }
+ }
+ };
+ }
+
+ public ContractEventAttribute(Hardfork activeIn, int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value)
+ {
+ ActiveIn = activeIn;
+ }
+
+ public ContractEventAttribute(int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value)
+ {
+ Order = order;
+ Descriptor = new ContractEventDescriptor()
+ {
+ Name = name,
+ Parameters = new ContractParameterDefinition[]
+ {
+ new ContractParameterDefinition()
+ {
+ Name = arg1Name,
+ Type = arg1Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg2Name,
+ Type = arg2Value
+ }
+ }
+ };
+ }
+
+ public ContractEventAttribute(Hardfork activeIn, int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value,
+ string arg3Name, ContractParameterType arg3Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value)
+ {
+ ActiveIn = activeIn;
+ }
+
+ public ContractEventAttribute(int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value,
+ string arg3Name, ContractParameterType arg3Value
+ )
+ {
+ Order = order;
+ Descriptor = new ContractEventDescriptor()
+ {
+ Name = name,
+ Parameters = new ContractParameterDefinition[]
+ {
+ new ContractParameterDefinition()
+ {
+ Name = arg1Name,
+ Type = arg1Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg2Name,
+ Type = arg2Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg3Name,
+ Type = arg3Value
+ }
+ }
+ };
+ }
+
+ public ContractEventAttribute(Hardfork activeIn, int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value,
+ string arg3Name, ContractParameterType arg3Value,
+ string arg4Name, ContractParameterType arg4Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value, arg4Name, arg4Value)
+ {
+ ActiveIn = activeIn;
+ }
+
+ public ContractEventAttribute(int order, string name,
+ string arg1Name, ContractParameterType arg1Value,
+ string arg2Name, ContractParameterType arg2Value,
+ string arg3Name, ContractParameterType arg3Value,
+ string arg4Name, ContractParameterType arg4Value
+ )
+ {
+ Order = order;
+ Descriptor = new ContractEventDescriptor()
+ {
+ Name = name,
+ Parameters = new ContractParameterDefinition[]
+ {
+ new ContractParameterDefinition()
+ {
+ Name = arg1Name,
+ Type = arg1Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg2Name,
+ Type = arg2Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg3Name,
+ Type = arg3Value
+ },
+ new ContractParameterDefinition()
+ {
+ Name = arg4Name,
+ Type = arg4Value
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs
index 0f846a37cc..3347163a6c 100644
--- a/src/Neo/SmartContract/Native/ContractManagement.cs
+++ b/src/Neo/SmartContract/Native/ContractManagement.cs
@@ -35,50 +35,10 @@ public sealed class ContractManagement : NativeContract
private const byte Prefix_Contract = 8;
private const byte Prefix_ContractHash = 12;
- internal ContractManagement()
- {
- var events = new List(Manifest.Abi.Events)
- {
- new ContractEventDescriptor
- {
- Name = "Deploy",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Hash",
- Type = ContractParameterType.Hash160
- }
- }
- },
- new ContractEventDescriptor
- {
- Name = "Update",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Hash",
- Type = ContractParameterType.Hash160
- }
- }
- },
- new ContractEventDescriptor
- {
- Name = "Destroy",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Hash",
- Type = ContractParameterType.Hash160
- }
- }
- }
- };
-
- Manifest.Abi.Events = events.ToArray();
- }
+ [ContractEvent(0, name: "Deploy", "Hash", ContractParameterType.Hash160)]
+ [ContractEvent(1, name: "Update", "Hash", ContractParameterType.Hash160)]
+ [ContractEvent(2, name: "Destroy", "Hash", ContractParameterType.Hash160)]
+ internal ContractManagement() : base() { }
private int GetNextAvailableId(DataCache snapshot)
{
@@ -88,10 +48,13 @@ private int GetNextAvailableId(DataCache snapshot)
return value;
}
- internal override ContractTask Initialize(ApplicationEngine engine)
+ internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork)
{
- engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000));
- engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1));
+ if (hardfork == ActiveIn)
+ {
+ engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1));
+ }
return ContractTask.CompletedTask;
}
@@ -107,17 +70,31 @@ internal override async ContractTask OnPersist(ApplicationEngine engine)
{
foreach (NativeContract contract in Contracts)
{
- if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index))
+ if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index, out Hardfork? hf))
{
- engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState
+ ContractState contractState = contract.GetContractState(engine.ProtocolSettings, engine.PersistingBlock.Index);
+ StorageItem state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(contract.Hash));
+
+ if (state is null)
+ {
+ // Create the contract state
+ engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(contractState));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray()));
+ }
+ else
{
- Id = contract.Id,
- Nef = contract.Nef,
- Hash = contract.Hash,
- Manifest = contract.Manifest
- }));
- engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray()));
- await contract.Initialize(engine);
+ // Parse old contract
+ var oldContract = state.GetInteroperable();
+ // Increase the update counter
+ oldContract.UpdateCounter++;
+ // Modify nef and manifest
+ oldContract.Nef = contractState.Nef;
+ oldContract.Manifest = contractState.Manifest;
+ }
+
+ await contract.Initialize(engine, hf);
+ // Emit native contract notification
+ engine.SendNotification(Hash, state is null ? "Deploy" : "Update", new VM.Types.Array(engine.ReferenceCounter) { contract.Hash.ToArray() });
}
}
}
diff --git a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs
index 55940f2eb3..cd8f7de59e 100644
--- a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs
+++ b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs
@@ -10,9 +10,11 @@
// modifications are permitted.
using System;
+using System.Diagnostics;
namespace Neo.SmartContract.Native
{
+ [DebuggerDisplay("{Name}")]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)]
internal class ContractMethodAttribute : Attribute
{
@@ -20,5 +22,13 @@ internal class ContractMethodAttribute : Attribute
public CallFlags RequiredCallFlags { get; init; }
public long CpuFee { get; init; }
public long StorageFee { get; init; }
+ public Hardfork? ActiveIn { get; init; } = null;
+
+ public ContractMethodAttribute() { }
+
+ public ContractMethodAttribute(Hardfork activeIn)
+ {
+ ActiveIn = activeIn;
+ }
}
}
diff --git a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs
index 83c24191fd..9f83eb20fc 100644
--- a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs
+++ b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs
@@ -15,6 +15,7 @@
using Neo.SmartContract.Manifest;
using Neo.VM.Types;
using System;
+using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Reflection;
@@ -22,6 +23,7 @@
namespace Neo.SmartContract.Native
{
+ [DebuggerDisplay("{Name}")]
internal class ContractMethodMetadata
{
public string Name { get; }
@@ -33,6 +35,7 @@ internal class ContractMethodMetadata
public long StorageFee { get; }
public CallFlags RequiredCallFlags { get; }
public ContractMethodDescriptor Descriptor { get; }
+ public Hardfork? ActiveIn { get; init; } = null;
public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute)
{
@@ -56,6 +59,7 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu
this.CpuFee = attribute.CpuFee;
this.StorageFee = attribute.StorageFee;
this.RequiredCallFlags = attribute.RequiredCallFlags;
+ this.ActiveIn = attribute.ActiveIn;
this.Descriptor = new ContractMethodDescriptor
{
Name = Name,
diff --git a/src/Neo/SmartContract/Native/CryptoLib.cs b/src/Neo/SmartContract/Native/CryptoLib.cs
index 257deaa72f..a7b822e39c 100644
--- a/src/Neo/SmartContract/Native/CryptoLib.cs
+++ b/src/Neo/SmartContract/Native/CryptoLib.cs
@@ -11,6 +11,7 @@
using Neo.Cryptography;
using Neo.Cryptography.ECC;
+using Org.BouncyCastle.Crypto.Digests;
using System;
using System.Collections.Generic;
@@ -27,7 +28,7 @@ public sealed partial class CryptoLib : NativeContract
[NamedCurve.secp256r1] = ECCurve.Secp256r1
};
- internal CryptoLib() { }
+ internal CryptoLib() : base() { }
///
/// Computes the hash value for the specified byte array using the ripemd160 algorithm.
@@ -64,6 +65,21 @@ public static byte[] Murmur32(byte[] data, uint seed)
return murmur.ComputeHash(data);
}
+ ///
+ /// Computes the hash value for the specified byte array using the keccak256 algorithm.
+ ///
+ /// The input to compute the hash code for.
+ /// Computed hash
+ [ContractMethod(Hardfork.HF_Cockatrice, CpuFee = 1 << 15)]
+ public static byte[] Keccak256(byte[] data)
+ {
+ KeccakDigest keccak = new(256);
+ keccak.BlockUpdate(data, 0, data.Length);
+ byte[] result = new byte[keccak.GetDigestSize()];
+ keccak.DoFinal(result, 0);
+ return result;
+ }
+
///
/// Verifies that a digital signature is appropriate for the provided key and message using the ECDSA algorithm.
///
diff --git a/src/Neo/SmartContract/Native/FungibleToken.cs b/src/Neo/SmartContract/Native/FungibleToken.cs
index 1a3a2ac532..6499c60c19 100644
--- a/src/Neo/SmartContract/Native/FungibleToken.cs
+++ b/src/Neo/SmartContract/Native/FungibleToken.cs
@@ -14,7 +14,6 @@
using Neo.SmartContract.Manifest;
using Neo.VM.Types;
using System;
-using System.Collections.Generic;
using System.Numerics;
using Array = Neo.VM.Types.Array;
@@ -57,39 +56,18 @@ public abstract class FungibleToken : NativeContract
///
/// Initializes a new instance of the class.
///
- protected FungibleToken()
+ [ContractEvent(0, name: "Transfer",
+ "from", ContractParameterType.Hash160,
+ "to", ContractParameterType.Hash160,
+ "amount", ContractParameterType.Integer)]
+ protected FungibleToken() : base()
{
this.Factor = BigInteger.Pow(10, Decimals);
+ }
- Manifest.SupportedStandards = new[] { "NEP-17" };
-
- var events = new List(Manifest.Abi.Events)
- {
- new ContractEventDescriptor
- {
- Name = "Transfer",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "from",
- Type = ContractParameterType.Hash160
- },
- new ContractParameterDefinition()
- {
- Name = "to",
- Type = ContractParameterType.Hash160
- },
- new ContractParameterDefinition()
- {
- Name = "amount",
- Type = ContractParameterType.Integer
- }
- }
- }
- };
-
- Manifest.Abi.Events = events.ToArray();
+ protected override void OnManifestCompose(ContractManifest manifest)
+ {
+ manifest.SupportedStandards = new[] { "NEP-17" };
}
internal async ContractTask Mint(ApplicationEngine engine, UInt160 account, BigInteger amount, bool callOnPayment)
diff --git a/src/Neo/SmartContract/Native/GasToken.cs b/src/Neo/SmartContract/Native/GasToken.cs
index 0c5a721edc..ce2f77ad2c 100644
--- a/src/Neo/SmartContract/Native/GasToken.cs
+++ b/src/Neo/SmartContract/Native/GasToken.cs
@@ -26,10 +26,14 @@ internal GasToken()
{
}
- internal override ContractTask Initialize(ApplicationEngine engine)
+ internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork)
{
- UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators);
- return Mint(engine, account, engine.ProtocolSettings.InitialGasDistribution, false);
+ if (hardfork == ActiveIn)
+ {
+ UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators);
+ return Mint(engine, account, engine.ProtocolSettings.InitialGasDistribution, false);
+ }
+ return ContractTask.CompletedTask;
}
internal override async ContractTask OnPersist(ApplicationEngine engine)
diff --git a/src/Neo/SmartContract/Native/LedgerContract.cs b/src/Neo/SmartContract/Native/LedgerContract.cs
index 3bb7568adf..d72f07c022 100644
--- a/src/Neo/SmartContract/Native/LedgerContract.cs
+++ b/src/Neo/SmartContract/Native/LedgerContract.cs
@@ -32,9 +32,7 @@ public sealed class LedgerContract : NativeContract
private const byte Prefix_Block = 5;
private const byte Prefix_Transaction = 11;
- internal LedgerContract()
- {
- }
+ internal LedgerContract() : base() { }
internal override ContractTask OnPersist(ApplicationEngine engine)
{
diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs
index 0229582310..6d5d105b7d 100644
--- a/src/Neo/SmartContract/Native/NativeContract.cs
+++ b/src/Neo/SmartContract/Native/NativeContract.cs
@@ -14,6 +14,8 @@
using Neo.VM;
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
@@ -24,9 +26,32 @@ namespace Neo.SmartContract.Native
///
public abstract class NativeContract
{
+ private class NativeContractsCache
+ {
+ public class CacheEntry
+ {
+ public Dictionary Methods { get; set; }
+ public byte[] Script { get; set; }
+ }
+
+ internal Dictionary NativeContracts { get; set; } = new Dictionary();
+
+ public CacheEntry GetAllowedMethods(NativeContract native, ApplicationEngine engine)
+ {
+ if (NativeContracts.TryGetValue(native.Id, out var value)) return value;
+
+ uint index = engine.PersistingBlock is null ? Ledger.CurrentIndex(engine.Snapshot) : engine.PersistingBlock.Index;
+ CacheEntry methods = native.GetAllowedMethods(engine.ProtocolSettings, index);
+ NativeContracts[native.Id] = methods;
+ return methods;
+ }
+ }
+
private static readonly List contractsList = new();
private static readonly Dictionary contractsDictionary = new();
- private readonly Dictionary methods = new();
+ private readonly ImmutableHashSet usedHardforks;
+ private readonly ReadOnlyCollection methodDescriptors;
+ private readonly ReadOnlyCollection eventsDescriptors;
private static int id_counter = 0;
#region Named Native Contracts
@@ -93,11 +118,6 @@ public abstract class NativeContract
///
public virtual Hardfork? ActiveIn { get; } = null;
- ///
- /// The nef of the native contract.
- ///
- public NefFile Nef { get; }
-
///
/// The hash of the native contract.
///
@@ -108,28 +128,58 @@ public abstract class NativeContract
///
public int Id { get; } = --id_counter;
- ///
- /// The manifest of the native contract.
- ///
- public ContractManifest Manifest { get; }
-
///
/// Initializes a new instance of the class.
///
protected NativeContract()
{
- List descriptors = new();
+ this.Hash = Helper.GetContractHash(UInt160.Zero, 0, Name);
+
+ // Reflection to get the methods
+
+ List listMethods = new();
foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
ContractMethodAttribute attribute = member.GetCustomAttribute();
if (attribute is null) continue;
- descriptors.Add(new ContractMethodMetadata(member, attribute));
+ listMethods.Add(new ContractMethodMetadata(member, attribute));
}
- descriptors = descriptors.OrderBy(p => p.Name, StringComparer.Ordinal).ThenBy(p => p.Parameters.Length).ToList();
+ methodDescriptors = listMethods.OrderBy(p => p.Name, StringComparer.Ordinal).ThenBy(p => p.Parameters.Length).ToList().AsReadOnly();
+
+ // Reflection to get the events
+ eventsDescriptors =
+ GetType().GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Array.Empty(), null)?.
+ GetCustomAttributes().
+ OrderBy(p => p.Order).ToList().AsReadOnly();
+
+ // Calculate the initializations forks
+ usedHardforks =
+ methodDescriptors.Select(u => u.ActiveIn)
+ .Concat(eventsDescriptors.Select(u => u.ActiveIn))
+ .Concat(new Hardfork?[] { ActiveIn })
+ .Where(u => u is not null)
+ .OrderBy(u => (byte)u)
+ .Cast().ToImmutableHashSet();
+
+ contractsList.Add(this);
+ contractsDictionary.Add(Hash, this);
+ }
+
+ ///
+ /// The allowed methods and his offsets.
+ ///
+ /// The where the HardForks are configured.
+ /// Block index
+ /// The .
+ private NativeContractsCache.CacheEntry GetAllowedMethods(ProtocolSettings settings, uint index)
+ {
+ Dictionary methods = new();
+
+ // Reflection to get the ContractMethods
byte[] script;
using (ScriptBuilder sb = new())
{
- foreach (ContractMethodMetadata method in descriptors)
+ foreach (ContractMethodMetadata method in methodDescriptors.Where(u => u.ActiveIn is null || settings.IsHardforkEnabled(u.ActiveIn.Value, index)))
{
method.Descriptor.Offset = sb.Length;
sb.EmitPush(0); //version
@@ -139,49 +189,99 @@ protected NativeContract()
}
script = sb.ToArray();
}
- this.Nef = new NefFile
+
+ return new NativeContractsCache.CacheEntry() { Methods = methods, Script = script };
+ }
+
+ ///
+ /// The of the native contract.
+ ///
+ /// The where the HardForks are configured.
+ /// Block index
+ /// The .
+ internal ContractState GetContractState(ProtocolSettings settings, uint index)
+ {
+ // Get allowed methods and nef script
+ NativeContractsCache.CacheEntry allowedMethods = GetAllowedMethods(settings, index);
+
+ // Compose nef file
+ NefFile nef = new()
{
Compiler = "neo-core-v3.0",
Source = string.Empty,
Tokens = Array.Empty(),
- Script = script
+ Script = allowedMethods.Script
};
- this.Nef.CheckSum = NefFile.ComputeChecksum(Nef);
- this.Hash = Helper.GetContractHash(UInt160.Zero, 0, Name);
- this.Manifest = new ContractManifest
+ nef.CheckSum = NefFile.ComputeChecksum(nef);
+
+ // Compose manifest
+ ContractManifest manifest = new()
{
Name = Name,
Groups = Array.Empty(),
SupportedStandards = Array.Empty(),
Abi = new ContractAbi()
{
- Events = Array.Empty(),
- Methods = descriptors.Select(p => p.Descriptor).ToArray()
+ Events = eventsDescriptors
+ .Where(u => u.ActiveIn is null || settings.IsHardforkEnabled(u.ActiveIn.Value, index))
+ .Select(p => p.Descriptor).ToArray(),
+ Methods = allowedMethods.Methods.Values
+ .Select(p => p.Descriptor).ToArray()
},
Permissions = new[] { ContractPermission.DefaultPermission },
Trusts = WildcardContainer.Create(),
Extra = null
};
- contractsList.Add(this);
- contractsDictionary.Add(Hash, this);
+
+ OnManifestCompose(manifest);
+
+ // Return ContractState
+ return new ContractState
+ {
+ Id = Id,
+ Nef = nef,
+ Hash = Hash,
+ Manifest = manifest
+ };
}
+ protected virtual void OnManifestCompose(ContractManifest manifest) { }
+
///
/// It is the initialize block
///
/// The where the HardForks are configured.
/// Block index
+ /// Active hardfork
/// True if the native contract must be initialized
- internal bool IsInitializeBlock(ProtocolSettings settings, uint index)
+ internal bool IsInitializeBlock(ProtocolSettings settings, uint index, out Hardfork? hardfork)
{
- if (ActiveIn is null) return index == 0;
+ // If is not configured, the Genesis is the a initialized block
+ if (index == 0 && ActiveIn is null)
+ {
+ hardfork = null;
+ return true;
+ }
- if (!settings.Hardforks.TryGetValue(ActiveIn.Value, out var activeIn))
+ // If is in the hardfork height, return true
+ foreach (Hardfork hf in usedHardforks)
{
- return false;
+ if (!settings.Hardforks.TryGetValue(hf, out var activeIn))
+ {
+ // If is not set in the configuration is treated as enabled from the genesis
+ activeIn = 0;
+ }
+
+ if (activeIn == index)
+ {
+ hardfork = hf;
+ return true;
+ }
}
- return activeIn == index;
+ // Initialized not required
+ hardfork = null;
+ return false;
}
///
@@ -196,7 +296,8 @@ internal bool IsActive(ProtocolSettings settings, uint index)
if (!settings.Hardforks.TryGetValue(ActiveIn.Value, out var activeIn))
{
- return false;
+ // If is not set in the configuration is treated as enabled from the genesis
+ activeIn = 0;
}
return activeIn <= index;
@@ -235,8 +336,14 @@ 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);
+ // Check if the method is allowed
ExecutionContext context = engine.CurrentContext;
- ContractMethodMetadata method = methods[context.InstructionPointer];
+ ContractMethodMetadata method = currentAllowedMethods.Methods[context.InstructionPointer];
+ if (method.ActiveIn is not null && !engine.IsHardforkEnabled(method.ActiveIn.Value))
+ throw new InvalidOperationException($"Cannot call this method before hardfork {method.ActiveIn}.");
ExecutionContextState state = context.GetState();
if (!state.CallFlags.HasFlag(method.RequiredCallFlags))
throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}.");
@@ -277,7 +384,7 @@ public static bool IsNative(UInt160 hash)
return contractsDictionary.ContainsKey(hash);
}
- internal virtual ContractTask Initialize(ApplicationEngine engine)
+ internal virtual ContractTask Initialize(ApplicationEngine engine, Hardfork? hardFork)
{
return ContractTask.CompletedTask;
}
diff --git a/src/Neo/SmartContract/Native/NeoToken.cs b/src/Neo/SmartContract/Native/NeoToken.cs
index 2fd26d04a7..ed796a9756 100644
--- a/src/Neo/SmartContract/Native/NeoToken.cs
+++ b/src/Neo/SmartContract/Native/NeoToken.cs
@@ -15,7 +15,6 @@
using Neo.IO;
using Neo.Persistence;
using Neo.SmartContract.Iterators;
-using Neo.SmartContract.Manifest;
using Neo.VM;
using Neo.VM.Types;
using System;
@@ -55,64 +54,18 @@ public sealed class NeoToken : FungibleToken
private const byte CommitteeRewardRatio = 10;
private const byte VoterRewardRatio = 80;
- internal NeoToken()
+ [ContractEvent(1, name: "CandidateStateChanged",
+ "pubkey", ContractParameterType.PublicKey,
+ "registered", ContractParameterType.Boolean,
+ "votes", ContractParameterType.Integer)]
+ [ContractEvent(2, name: "Vote",
+ "account", ContractParameterType.Hash160,
+ "from", ContractParameterType.PublicKey,
+ "to", ContractParameterType.PublicKey,
+ "amount", ContractParameterType.Integer)]
+ internal NeoToken() : base()
{
this.TotalAmount = 100000000 * Factor;
-
- var events = new List(Manifest.Abi.Events)
- {
- new ContractEventDescriptor
- {
- Name = "CandidateStateChanged",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "pubkey",
- Type = ContractParameterType.PublicKey
- },
- new ContractParameterDefinition()
- {
- Name = "registered",
- Type = ContractParameterType.Boolean
- },
- new ContractParameterDefinition()
- {
- Name = "votes",
- Type = ContractParameterType.Integer
- }
- }
- },
- new ContractEventDescriptor
- {
- Name = "Vote",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "account",
- Type = ContractParameterType.Hash160
- },
- new ContractParameterDefinition()
- {
- Name = "from",
- Type = ContractParameterType.PublicKey
- },
- new ContractParameterDefinition()
- {
- Name = "to",
- Type = ContractParameterType.PublicKey
- },
- new ContractParameterDefinition()
- {
- Name = "amount",
- Type = ContractParameterType.Integer
- }
- }
- }
- };
-
- Manifest.Abi.Events = events.ToArray();
}
public override BigInteger TotalSupply(DataCache snapshot)
@@ -220,14 +173,18 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c
/// if the votes should be recounted; otherwise, .
public static bool ShouldRefreshCommittee(uint height, int committeeMembersCount) => height % committeeMembersCount == 0;
- internal override ContractTask Initialize(ApplicationEngine engine)
+ internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork)
{
- var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero)));
- engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee));
- engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(System.Array.Empty()));
- engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor));
- engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor));
- return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false);
+ if (hardfork == ActiveIn)
+ {
+ var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero)));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(System.Array.Empty()));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor));
+ return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false);
+ }
+ return ContractTask.CompletedTask;
}
internal override ContractTask OnPersist(ApplicationEngine engine)
diff --git a/src/Neo/SmartContract/Native/OracleContract.cs b/src/Neo/SmartContract/Native/OracleContract.cs
index 42f41f5720..d8415fca21 100644
--- a/src/Neo/SmartContract/Native/OracleContract.cs
+++ b/src/Neo/SmartContract/Native/OracleContract.cs
@@ -15,7 +15,6 @@
using Neo.IO;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
-using Neo.SmartContract.Manifest;
using Neo.VM;
using Neo.VM.Types;
using System;
@@ -41,58 +40,15 @@ public sealed class OracleContract : NativeContract
private const byte Prefix_Request = 7;
private const byte Prefix_IdList = 6;
- internal OracleContract()
- {
- var events = new List(Manifest.Abi.Events)
- {
- new ContractEventDescriptor
- {
- Name = "OracleRequest",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Id",
- Type = ContractParameterType.Integer
- },
- new ContractParameterDefinition()
- {
- Name = "RequestContract",
- Type = ContractParameterType.Hash160
- },
- new ContractParameterDefinition()
- {
- Name = "Url",
- Type = ContractParameterType.String
- },
- new ContractParameterDefinition()
- {
- Name = "Filter",
- Type = ContractParameterType.String
- }
- }
- },
- new ContractEventDescriptor
- {
- Name = "OracleResponse",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Id",
- Type = ContractParameterType.Integer
- },
- new ContractParameterDefinition()
- {
- Name = "OriginalTx",
- Type = ContractParameterType.Hash256
- }
- }
- }
- };
-
- Manifest.Abi.Events = events.ToArray();
- }
+ [ContractEvent(0, name: "OracleRequest",
+ "Id", ContractParameterType.Integer,
+ "RequestContract", ContractParameterType.Hash160,
+ "Url", ContractParameterType.String,
+ "Filter", ContractParameterType.String)]
+ [ContractEvent(1, name: "OracleResponse",
+ "Id", ContractParameterType.Integer,
+ "OriginalTx", ContractParameterType.Hash256)]
+ internal OracleContract() : base() { }
[ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)]
private void SetPrice(ApplicationEngine engine, long price)
@@ -178,10 +134,13 @@ private static byte[] GetUrlHash(string url)
return Crypto.Hash160(Utility.StrictUTF8.GetBytes(url));
}
- internal override ContractTask Initialize(ApplicationEngine engine)
+ internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork)
{
- engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero));
- engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000));
+ if (hardfork == ActiveIn)
+ {
+ engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000));
+ }
return ContractTask.CompletedTask;
}
diff --git a/src/Neo/SmartContract/Native/PolicyContract.cs b/src/Neo/SmartContract/Native/PolicyContract.cs
index c633cb7309..5b00af92ad 100644
--- a/src/Neo/SmartContract/Native/PolicyContract.cs
+++ b/src/Neo/SmartContract/Native/PolicyContract.cs
@@ -64,15 +64,16 @@ public sealed class PolicyContract : NativeContract
private const byte Prefix_StoragePrice = 19;
private const byte Prefix_AttributeFee = 20;
- internal PolicyContract()
- {
- }
+ internal PolicyContract() : base() { }
- internal override ContractTask Initialize(ApplicationEngine engine)
+ internal override ContractTask Initialize(ApplicationEngine engine, Hardfork? hardfork)
{
- engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte));
- engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor));
- engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice));
+ if (hardfork == ActiveIn)
+ {
+ engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor));
+ engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice));
+ }
return ContractTask.CompletedTask;
}
diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs
index 1c2fa0a299..6e989b78cf 100644
--- a/src/Neo/SmartContract/Native/RoleManagement.cs
+++ b/src/Neo/SmartContract/Native/RoleManagement.cs
@@ -12,11 +12,9 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Persistence;
-using Neo.SmartContract.Manifest;
using Neo.VM;
using Neo.VM.Types;
using System;
-using System.Collections.Generic;
using System.Linq;
namespace Neo.SmartContract.Native
@@ -26,31 +24,10 @@ namespace Neo.SmartContract.Native
///
public sealed class RoleManagement : NativeContract
{
- internal RoleManagement()
- {
- var events = new List(Manifest.Abi.Events)
- {
- new ContractEventDescriptor
- {
- Name = "Designation",
- Parameters = new ContractParameterDefinition[]
- {
- new ContractParameterDefinition()
- {
- Name = "Role",
- Type = ContractParameterType.Integer
- },
- new ContractParameterDefinition()
- {
- Name = "BlockIndex",
- Type = ContractParameterType.Integer
- }
- }
- }
- };
-
- Manifest.Abi.Events = events.ToArray();
- }
+ [ContractEvent(0, name: "Designation",
+ "Role", ContractParameterType.Integer,
+ "BlockIndex", ContractParameterType.Integer)]
+ internal RoleManagement() : base() { }
///
/// Gets the list of nodes for the specified role.
diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs
index 3ca836a3ec..f8fa9efcc2 100644
--- a/src/Neo/SmartContract/Native/StdLib.cs
+++ b/src/Neo/SmartContract/Native/StdLib.cs
@@ -27,7 +27,7 @@ public sealed class StdLib : NativeContract
{
private const int MaxInputLength = 1024;
- internal StdLib() { }
+ internal StdLib() : base() { }
[ContractMethod(CpuFee = 1 << 12)]
private static byte[] Serialize(ApplicationEngine engine, StackItem item)
diff --git a/src/Neo/SmartContract/StorageItem.cs b/src/Neo/SmartContract/StorageItem.cs
index 0f13c3c1f0..41486997de 100644
--- a/src/Neo/SmartContract/StorageItem.cs
+++ b/src/Neo/SmartContract/StorageItem.cs
@@ -160,6 +160,16 @@ public void Set(BigInteger integer)
value = null;
}
+ ///
+ /// Sets the interoperable value of the storage.
+ ///
+ /// The value of the .
+ public void Set(IInteroperable interoperable)
+ {
+ cache = interoperable;
+ value = null;
+ }
+
public static implicit operator BigInteger(StorageItem item)
{
item.cache ??= new BigInteger(item.value.Span);
diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs
new file mode 100644
index 0000000000..ba8f703253
--- /dev/null
+++ b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs
@@ -0,0 +1,151 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// UT_ContractEventAttribute.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 Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.SmartContract;
+using Neo.SmartContract.Native;
+
+namespace Neo.UnitTests.SmartContract.Native
+{
+ [TestClass]
+ public class UT_ContractEventAttribute
+ {
+ [TestMethod]
+ public void TestConstructorOneArg()
+ {
+ var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "1", "a1", ContractParameterType.String);
+
+ Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn);
+ Assert.AreEqual(0, arg.Order);
+ Assert.AreEqual("1", arg.Descriptor.Name);
+ Assert.AreEqual(1, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+
+ arg = new ContractEventAttribute(1, "1", "a1", ContractParameterType.String);
+
+ Assert.IsNull(arg.ActiveIn);
+ Assert.AreEqual(1, arg.Order);
+ Assert.AreEqual("1", arg.Descriptor.Name);
+ Assert.AreEqual(1, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ }
+
+ [TestMethod]
+ public void TestConstructorTwoArg()
+ {
+ var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "2",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer);
+
+ Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn);
+ Assert.AreEqual(0, arg.Order);
+ Assert.AreEqual("2", arg.Descriptor.Name);
+ Assert.AreEqual(2, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+
+ arg = new ContractEventAttribute(1, "2",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer);
+
+ Assert.IsNull(arg.ActiveIn);
+ Assert.AreEqual(1, arg.Order);
+ Assert.AreEqual("2", arg.Descriptor.Name);
+ Assert.AreEqual(2, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+ }
+
+ [TestMethod]
+ public void TestConstructorThreeArg()
+ {
+ var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "3",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer,
+ "a3", ContractParameterType.Boolean);
+
+ Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn);
+ Assert.AreEqual(0, arg.Order);
+ Assert.AreEqual("3", arg.Descriptor.Name);
+ Assert.AreEqual(3, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+ Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name);
+ Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type);
+
+ arg = new ContractEventAttribute(1, "3",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer,
+ "a3", ContractParameterType.Boolean);
+
+ Assert.IsNull(arg.ActiveIn);
+ Assert.AreEqual(1, arg.Order);
+ Assert.AreEqual("3", arg.Descriptor.Name);
+ Assert.AreEqual(3, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+ Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name);
+ Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type);
+ }
+
+ [TestMethod]
+ public void TestConstructorFourArg()
+ {
+ var arg = new ContractEventAttribute(Hardfork.HF_Basilisk, 0, "4",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer,
+ "a3", ContractParameterType.Boolean,
+ "a4", ContractParameterType.Array);
+
+ Assert.AreEqual(Hardfork.HF_Basilisk, arg.ActiveIn);
+ Assert.AreEqual(0, arg.Order);
+ Assert.AreEqual("4", arg.Descriptor.Name);
+ Assert.AreEqual(4, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+ Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name);
+ Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type);
+ Assert.AreEqual("a4", arg.Descriptor.Parameters[3].Name);
+ Assert.AreEqual(ContractParameterType.Array, arg.Descriptor.Parameters[3].Type);
+
+ arg = new ContractEventAttribute(1, "4",
+ "a1", ContractParameterType.String,
+ "a2", ContractParameterType.Integer,
+ "a3", ContractParameterType.Boolean,
+ "a4", ContractParameterType.Array);
+
+ Assert.IsNull(arg.ActiveIn);
+ Assert.AreEqual(1, arg.Order);
+ Assert.AreEqual("4", arg.Descriptor.Name);
+ Assert.AreEqual(4, arg.Descriptor.Parameters.Length);
+ Assert.AreEqual("a1", arg.Descriptor.Parameters[0].Name);
+ Assert.AreEqual(ContractParameterType.String, arg.Descriptor.Parameters[0].Type);
+ Assert.AreEqual("a2", arg.Descriptor.Parameters[1].Name);
+ Assert.AreEqual(ContractParameterType.Integer, arg.Descriptor.Parameters[1].Type);
+ Assert.AreEqual("a3", arg.Descriptor.Parameters[2].Name);
+ Assert.AreEqual(ContractParameterType.Boolean, arg.Descriptor.Parameters[2].Type);
+ Assert.AreEqual("a4", arg.Descriptor.Parameters[3].Name);
+ Assert.AreEqual(ContractParameterType.Array, arg.Descriptor.Parameters[3].Type);
+ }
+ }
+}
diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs
new file mode 100644
index 0000000000..a2b746d7c8
--- /dev/null
+++ b/tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs
@@ -0,0 +1,32 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// UT_ContractMethodAttribute.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 Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.SmartContract.Native;
+
+namespace Neo.UnitTests.SmartContract.Native
+{
+ [TestClass]
+ public class UT_ContractMethodAttribute
+ {
+ [TestMethod]
+ public void TestConstructorOneArg()
+ {
+ var arg = new ContractMethodAttribute();
+
+ Assert.IsNull(arg.ActiveIn);
+
+ arg = new ContractMethodAttribute(Hardfork.HF_Aspidochelone);
+
+ Assert.AreEqual(Hardfork.HF_Aspidochelone, arg.ActiveIn);
+ }
+ }
+}
diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs
index 9e36488031..6f150cca12 100644
--- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs
+++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs
@@ -15,6 +15,7 @@
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
+using Org.BouncyCastle.Utilities.Encoders;
namespace Neo.UnitTests.SmartContract.Native
{
@@ -334,5 +335,97 @@ public void TestBls12381ScalarMul_Compat()
BLS12381PointType.G2Proj
);
}
+
+ ///
+ /// Keccak256 cases are verified in https://emn178.github.io/online-tools/keccak_256.html
+ ///
+ [TestMethod]
+ public void TestKeccak256_HelloWorld()
+ {
+ // Arrange
+ byte[] inputData = "Hello, World!"u8.ToArray();
+ string expectedHashHex = "acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Hello, World!'.");
+ }
+ [TestMethod]
+ public void TestKeccak256_Keccak()
+ {
+ // Arrange
+ byte[] inputData = "Keccak"u8.ToArray();
+ string expectedHashHex = "868c016b666c7d3698636ee1bd023f3f065621514ab61bf26f062c175fdbe7f2";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Keccak'.");
+ }
+
+ [TestMethod]
+ public void TestKeccak256_Cryptography()
+ {
+ // Arrange
+ byte[] inputData = "Cryptography"u8.ToArray();
+ string expectedHashHex = "53d49d225dd2cfe77d8c5e2112bcc9efe77bea1c7aa5e5ede5798a36e99e2d29";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Cryptography'.");
+ }
+
+ [TestMethod]
+ public void TestKeccak256_Testing123()
+ {
+ // Arrange
+ byte[] inputData = "Testing123"u8.ToArray();
+ string expectedHashHex = "3f82db7b16b0818a1c6b2c6152e265f682d5ebcf497c9aad776ad38bc39cb6ca";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for 'Testing123'.");
+ }
+
+ [TestMethod]
+ public void TestKeccak256_LongString()
+ {
+ // Arrange
+ byte[] inputData = "This is a longer string for Keccak256 testing purposes."u8.ToArray();
+ string expectedHashHex = "24115e5c2359f85f6840b42acd2f7ea47bc239583e576d766fa173bf711bdd2f";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for the longer string.");
+ }
+
+ [TestMethod]
+ public void TestKeccak256_BlankString()
+ {
+ // Arrange
+ byte[] inputData = ""u8.ToArray();
+ string expectedHashHex = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
+
+ // Act
+ byte[] outputData = CryptoLib.Keccak256(inputData);
+ string outputHashHex = Hex.ToHexString(outputData);
+
+ // Assert
+ Assert.AreEqual(expectedHashHex, outputHashHex, "Keccak256 hash did not match expected value for blank string.");
+ }
}
}
diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs
index 5f4b5b43e0..7f23519e9f 100644
--- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs
+++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs
@@ -11,6 +11,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.Native;
+using System.IO;
namespace Neo.UnitTests.SmartContract.Native
{
@@ -22,5 +23,25 @@ public void TestGetContract()
{
Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash));
}
+
+ [TestMethod]
+ public void TestIsInitializeBlock()
+ {
+ string json = UT_ProtocolSettings.CreateHKSettings("\"HF_Cockatrice\": 20");
+
+ var file = Path.GetTempFileName();
+ File.WriteAllText(file, json);
+ ProtocolSettings settings = ProtocolSettings.Load(file, false);
+ File.Delete(file);
+
+ Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 0, out var hf));
+ Assert.IsNull(hf);
+
+ Assert.IsFalse(NativeContract.CryptoLib.IsInitializeBlock(settings, 1, out hf));
+ Assert.IsNull(hf);
+
+ Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 20, out hf));
+ Assert.AreEqual(Hardfork.HF_Cockatrice, hf);
+ }
}
}
diff --git a/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
index 3798eab327..a0ab12fdc3 100644
--- a/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
+++ b/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
@@ -76,7 +76,7 @@ public void Runtime_GetNotifications_Test()
// Wrong length
- using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot))
+ using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default))
using (var script = new ScriptBuilder())
{
// Retrive
@@ -93,7 +93,7 @@ public void Runtime_GetNotifications_Test()
// All test
- using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot))
+ using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default))
using (var script = new ScriptBuilder())
{
// Notification
@@ -162,7 +162,7 @@ public void Runtime_GetNotifications_Test()
// Script notifications
- using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot))
+ using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, ProtocolSettings.Default))
using (var script = new ScriptBuilder())
{
// Notification
diff --git a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
index 3ab32ad7a5..fc29c95c12 100644
--- a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
+++ b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
@@ -272,7 +272,7 @@ public void Serialize_Map_Test()
[TestMethod]
public void Deserialize_Map_Test()
{
- ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null);
+ ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default);
var items = JsonSerializer.Deserialize(engine, JObject.Parse("{\"test1\":123,\"test2\":321}"), ExecutionEngineLimits.Default);
Assert.IsInstanceOfType(items, typeof(Map));
@@ -302,7 +302,7 @@ public void Serialize_Array_Bool_Str_Num()
[TestMethod]
public void Deserialize_Array_Bool_Str_Num()
{
- ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null);
+ ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, 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 +333,7 @@ public void Serialize_Array_OfArray()
[TestMethod]
public void Deserialize_Array_OfArray()
{
- ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null);
+ ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, 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/UT_ProtocolSettings.cs b/tests/Neo.UnitTests/UT_ProtocolSettings.cs
index 28817bb482..e5d829ef8b 100644
--- a/tests/Neo.UnitTests/UT_ProtocolSettings.cs
+++ b/tests/Neo.UnitTests/UT_ProtocolSettings.cs
@@ -14,6 +14,7 @@
using Neo.Cryptography.ECC;
using Neo.Wallets;
using System;
+using System.IO;
namespace Neo.UnitTests
{
@@ -48,6 +49,132 @@ public void TestGetMillisecondsPerBlock()
TestProtocolSettings.Default.MillisecondsPerBlock.Should().Be(15000);
}
+ [TestMethod]
+ public void HardForkTestBAndNotA()
+ {
+ string json = CreateHKSettings("\"HF_Basilisk\": 4120000");
+
+ var file = Path.GetTempFileName();
+ File.WriteAllText(file, json);
+ ProtocolSettings settings = ProtocolSettings.Load(file, false);
+ File.Delete(file);
+
+ settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0);
+ settings.Hardforks[Hardfork.HF_Basilisk].Should().Be(4120000);
+
+ // Check IsHardforkEnabled
+
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeFalse();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeFalse();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 4120000).Should().BeTrue();
+ }
+
+ [TestMethod]
+ public void HardForkTestAAndNotB()
+ {
+ string json = CreateHKSettings("\"HF_Aspidochelone\": 0");
+
+ var file = Path.GetTempFileName();
+ File.WriteAllText(file, json);
+ ProtocolSettings settings = ProtocolSettings.Load(file, false);
+ File.Delete(file);
+
+ settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0);
+ settings.Hardforks.ContainsKey(Hardfork.HF_Basilisk).Should().BeFalse();
+
+ // Check IsHardforkEnabled
+
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeFalse();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeFalse();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 4120000).Should().BeFalse();
+ }
+
+ [TestMethod]
+ public void HardForkTestNone()
+ {
+ string json = CreateHKSettings("");
+
+ var file = Path.GetTempFileName();
+ File.WriteAllText(file, json);
+ ProtocolSettings settings = ProtocolSettings.Load(file, false);
+ File.Delete(file);
+
+ settings.Hardforks[Hardfork.HF_Aspidochelone].Should().Be(0);
+ settings.Hardforks[Hardfork.HF_Basilisk].Should().Be(0);
+
+ // Check IsHardforkEnabled
+
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 0).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Aspidochelone, 10).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 0).Should().BeTrue();
+ settings.IsHardforkEnabled(Hardfork.HF_Basilisk, 10).Should().BeTrue();
+ }
+
+ [TestMethod]
+ public void HardForkTestAMoreThanB()
+ {
+ string json = CreateHKSettings("\"HF_Aspidochelone\": 4120001, \"HF_Basilisk\": 4120000");
+ var file = Path.GetTempFileName();
+ File.WriteAllText(file, json);
+ Assert.ThrowsException(() => ProtocolSettings.Load(file, false));
+ File.Delete(file);
+ }
+
+ internal static string CreateHKSettings(string hf)
+ {
+ return @"
+{
+ ""ProtocolConfiguration"": {
+ ""Network"": 860833102,
+ ""AddressVersion"": 53,
+ ""MillisecondsPerBlock"": 15000,
+ ""MaxTransactionsPerBlock"": 512,
+ ""MemoryPoolMaxTransactions"": 50000,
+ ""MaxTraceableBlocks"": 2102400,
+ ""Hardforks"": {
+ " + hf + @"
+ },
+ ""InitialGasDistribution"": 5200000000000000,
+ ""ValidatorsCount"": 7,
+ ""StandbyCommittee"": [
+ ""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",
+ ""02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"",
+ ""03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"",
+ ""02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"",
+ ""024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"",
+ ""02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"",
+ ""02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"",
+ ""023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"",
+ ""03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379"",
+ ""03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050"",
+ ""03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0"",
+ ""02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62"",
+ ""03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0"",
+ ""0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654"",
+ ""020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"",
+ ""0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"",
+ ""03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde"",
+ ""02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"",
+ ""0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"",
+ ""03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc"",
+ ""02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a""
+ ],
+ ""SeedList"": [
+ ""seed1.neo.org:10333"",
+ ""seed2.neo.org:10333"",
+ ""seed3.neo.org:10333"",
+ ""seed4.neo.org:10333"",
+ ""seed5.neo.org:10333""
+ ]
+ }
+}
+";
+ }
+
[TestMethod]
public void TestGetSeedList()
{