diff --git a/SpellingExclusions.dic b/SpellingExclusions.dic
index e69de29bb2..f186c01c26 100644
--- a/SpellingExclusions.dic
+++ b/SpellingExclusions.dic
@@ -0,0 +1,2 @@
+barc
+scripthash
diff --git a/neo.sln b/neo.sln
index b0de1c27b1..4ef3a10680 100644
--- a/neo.sln
+++ b/neo.sln
@@ -40,6 +40,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.IO", "src\Neo.IO\Neo.IO
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Extensions", "src\Neo.Extensions\Neo.Extensions.csproj", "{9C5213D6-3833-4570-8AE2-47E9F9017A8F}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Hosting.App", "src\Neo.Hosting.App\Neo.Hosting.App.csproj", "{DF99D759-46D5-4CDB-89CB-EF56DC9728DA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Hosting.App.Tests", "tests\Neo.Hosting.App.Tests\Neo.Hosting.App.Tests.csproj", "{FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{C2DC830A-327A-42A7-807D-295216D30DBB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Cryptography.MPTTrie.Tests", "tests\Neo.Cryptography.MPTTrie.Tests\Neo.Cryptography.MPTTrie.Tests.csproj", "{FAF5D8AC-B6B3-4CD4-879D-0E5F6211480F}"
@@ -50,8 +54,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.OracleService.T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.RpcServer.Tests", "tests\Neo.Plugins.RpcServer.Tests\Neo.Plugins.RpcServer.Tests.csproj", "{2CBD2311-BA2E-4921-A000-FDDA59B74958}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.Storage.Tests", "tests\Neo.Plugins.Storage.Tests\Neo.Plugins.Storage.Tests.csproj", "{EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7F257712-D033-47FF-B439-9D4320D06599}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationLogs", "src\Plugins\ApplicationLogs\ApplicationLogs.csproj", "{22E2CE64-080B-4138-885F-7FA74A9159FB}"
@@ -78,6 +80,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensTracker", "src\Plugin
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RpcClient", "src\Plugins\RpcClient\RpcClient.csproj", "{185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.Storage.Tests", "tests\Neo.Plugins.Storage.Tests\Neo.Plugins.Storage.Tests.csproj", "{9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -148,6 +152,14 @@ Global
{9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C5213D6-3833-4570-8AE2-47E9F9017A8F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF99D759-46D5-4CDB-89CB-EF56DC9728DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DF99D759-46D5-4CDB-89CB-EF56DC9728DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DF99D759-46D5-4CDB-89CB-EF56DC9728DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DF99D759-46D5-4CDB-89CB-EF56DC9728DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228}.Release|Any CPU.Build.0 = Release|Any CPU
{FAF5D8AC-B6B3-4CD4-879D-0E5F6211480F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAF5D8AC-B6B3-4CD4-879D-0E5F6211480F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAF5D8AC-B6B3-4CD4-879D-0E5F6211480F}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -164,10 +176,6 @@ Global
{2CBD2311-BA2E-4921-A000-FDDA59B74958}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CBD2311-BA2E-4921-A000-FDDA59B74958}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CBD2311-BA2E-4921-A000-FDDA59B74958}.Release|Any CPU.Build.0 = Release|Any CPU
- {EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C}.Release|Any CPU.Build.0 = Release|Any CPU
{22E2CE64-080B-4138-885F-7FA74A9159FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22E2CE64-080B-4138-885F-7FA74A9159FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22E2CE64-080B-4138-885F-7FA74A9159FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -216,6 +224,10 @@ Global
{185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -237,11 +249,12 @@ Global
{387CCF6C-9A26-43F6-A639-0A82E91E10D8} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
{4CDAC1AA-45C6-4157-8D8E-199050433048} = {B5339DF7-5D1D-43BA-B332-74B825E1770E}
{9C5213D6-3833-4570-8AE2-47E9F9017A8F} = {B5339DF7-5D1D-43BA-B332-74B825E1770E}
+ {DF99D759-46D5-4CDB-89CB-EF56DC9728DA} = {B5339DF7-5D1D-43BA-B332-74B825E1770E}
+ {FB1FA9F6-B6B4-48B7-A522-1FAF55DFB228} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
{FAF5D8AC-B6B3-4CD4-879D-0E5F6211480F} = {7F257712-D033-47FF-B439-9D4320D06599}
{0E92F219-1225-4DD0-8C4A-98840985D59C} = {7F257712-D033-47FF-B439-9D4320D06599}
{5D9764FB-827D-4DDE-84E3-3C05FD8ABC89} = {7F257712-D033-47FF-B439-9D4320D06599}
{2CBD2311-BA2E-4921-A000-FDDA59B74958} = {7F257712-D033-47FF-B439-9D4320D06599}
- {EF01E062-DBBC-47AF-AF3B-9EDEB00CFF7C} = {7F257712-D033-47FF-B439-9D4320D06599}
{7F257712-D033-47FF-B439-9D4320D06599} = {C2DC830A-327A-42A7-807D-295216D30DBB}
{22E2CE64-080B-4138-885F-7FA74A9159FB} = {C2DC830A-327A-42A7-807D-295216D30DBB}
{4C39E872-FC37-4BFD-AE4C-3E3F0546B726} = {C2DC830A-327A-42A7-807D-295216D30DBB}
@@ -255,6 +268,7 @@ Global
{FF76D8A4-356B-461A-8471-BC1B83E57BBC} = {C2DC830A-327A-42A7-807D-295216D30DBB}
{5E4947F3-05D3-4806-B0F3-30DAC71B5986} = {C2DC830A-327A-42A7-807D-295216D30DBB}
{185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0} = {C2DC830A-327A-42A7-807D-295216D30DBB}
+ {9B6BC5EC-EC11-408C-BFBA-C7ABD732F5B8} = {7F257712-D033-47FF-B439-9D4320D06599}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC}
diff --git a/src/Neo.Extensions/AssemblyUtility.cs b/src/Neo.Extensions/AssemblyUtility.cs
new file mode 100644
index 0000000000..f7cc10678b
--- /dev/null
+++ b/src/Neo.Extensions/AssemblyUtility.cs
@@ -0,0 +1,25 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// AssemblyUtility.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 System.Reflection;
+
+namespace Neo.Extensions
+{
+ public static class AssemblyUtility
+ {
+ public static int GetVersionNumber()
+ {
+ var version = Assembly.GetCallingAssembly().GetName().Version;
+ if (version is null) return 0;
+ return version.Major * 1000 + version.Minor * 100 + version.Build * 10 + version.Revision;
+ }
+ }
+}
diff --git a/src/Neo.Extensions/LogLevel.cs b/src/Neo.Extensions/LogLevel.cs
index 5e3acca37c..5dc3bf81f1 100644
--- a/src/Neo.Extensions/LogLevel.cs
+++ b/src/Neo.Extensions/LogLevel.cs
@@ -41,6 +41,11 @@ public enum LogLevel : byte
///
/// The fatal log level.
///
- Fatal = Error + 1
+ Fatal = Error + 1,
+
+ ///
+ /// The trace log level.
+ ///
+ Trace = Fatal + 1,
}
}
diff --git a/src/Neo.Hosting.App/Buffers/MemoryPoolBlock.cs b/src/Neo.Hosting.App/Buffers/MemoryPoolBlock.cs
new file mode 100644
index 0000000000..98f1d3dc08
--- /dev/null
+++ b/src/Neo.Hosting.App/Buffers/MemoryPoolBlock.cs
@@ -0,0 +1,44 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// MemoryPoolBlock.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 System;
+using System.Buffers;
+using System.Runtime.InteropServices;
+
+namespace Neo.Hosting.App.Buffers
+{
+ internal sealed class MemoryPoolBlock : IMemoryOwner
+ {
+ public PinnedBlockMemoryPool Pool { get; }
+
+ internal MemoryPoolBlock(
+ PinnedBlockMemoryPool pool,
+ int length)
+ {
+ Pool = pool;
+
+ var pinnedArray = GC.AllocateUninitializedArray(length, pinned: true);
+
+ Memory = MemoryMarshal.CreateFromPinnedArray(pinnedArray, 0, pinnedArray.Length);
+ }
+
+ #region IMemoryOwner
+
+ public Memory Memory { get; }
+
+ void IDisposable.Dispose()
+ {
+ Pool.Return(this);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Neo.Hosting.App/Buffers/PinnedBlockMemoryPool.cs b/src/Neo.Hosting.App/Buffers/PinnedBlockMemoryPool.cs
new file mode 100644
index 0000000000..9881d8b8b9
--- /dev/null
+++ b/src/Neo.Hosting.App/Buffers/PinnedBlockMemoryPool.cs
@@ -0,0 +1,68 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// PinnedBlockMemoryPool.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 System;
+using System.Buffers;
+using System.Collections.Concurrent;
+
+namespace Neo.Hosting.App.Buffers
+{
+ internal sealed class PinnedBlockMemoryPool : MemoryPool
+ {
+ private const int AnySize = -1;
+
+ private static readonly int s_blockSize = 4096;
+
+ public static int BlockSize => s_blockSize;
+
+ public override int MaxBufferSize { get; } = s_blockSize;
+
+ private readonly ConcurrentQueue _blocks = new();
+ private readonly object _disposedSync = new();
+
+ private bool _isDisposed;
+
+ public override IMemoryOwner Rent(int size = AnySize)
+ {
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(size, s_blockSize);
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+
+ if (_blocks.TryDequeue(out var block))
+ return block;
+
+ return new MemoryPoolBlock(this, BlockSize);
+ }
+
+ internal void Return(MemoryPoolBlock block)
+ {
+ if (_isDisposed == false)
+ _blocks.Enqueue(block);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ lock (_disposedSync)
+ {
+ _isDisposed = true;
+
+ if (disposing)
+ {
+ while (_blocks.TryDequeue(out _))
+ {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Neo.Hosting.App/Buffers/Struffer.cs b/src/Neo.Hosting.App/Buffers/Struffer.cs
new file mode 100644
index 0000000000..d9a56a1cda
--- /dev/null
+++ b/src/Neo.Hosting.App/Buffers/Struffer.cs
@@ -0,0 +1,136 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Struffer.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 System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Neo.Hosting.App.Buffers
+{
+ ///
+ /// Structure byte buffer
+ ///
+ internal sealed class Struffer : IEnumerable
+ {
+ private static readonly UTF8Encoding s_utf8NoBom = new(false, true);
+
+ private byte[] _data;
+
+ public int Position { get; set; }
+
+ public Struffer()
+ {
+ _data = [];
+ Position = 0;
+ }
+
+ public Struffer(byte[] buffer)
+ {
+ _data = buffer;
+ }
+
+ public Struffer(int capacity)
+ {
+ _data = GC.AllocateUninitializedArray(capacity);
+ Position = 0;
+ }
+
+ public static int SizeOf(string value) =>
+ s_utf8NoBom.GetByteCount(value) + sizeof(int);
+
+ public Struffer Write(T value)
+ where T : unmanaged
+ {
+ var typeSize = Unsafe.SizeOf();
+
+ if (Position + typeSize > _data.Length)
+ Array.Resize(ref _data, _data.Length + typeSize);
+
+ Unsafe.As(ref _data[Position]) = value;
+
+ Position += typeSize;
+ return this;
+ }
+
+ public Struffer Write(T[] values)
+ where T : unmanaged
+ {
+ Write(values.Length);
+ foreach (var value in values)
+ Write(value);
+ return this;
+ }
+
+ public Struffer Write(string value)
+ {
+ var strByteCount = s_utf8NoBom.GetByteCount(value);
+ Write(strByteCount);
+
+ if (Position + strByteCount > _data.Length)
+ Array.Resize(ref _data, _data.Length + strByteCount);
+
+ Position += s_utf8NoBom.GetBytes(value, _data.AsSpan(Position));
+ return this;
+ }
+
+ public T Read()
+ where T : unmanaged
+ {
+ var typeSize = Unsafe.SizeOf();
+
+ if (Position + typeSize > _data.Length)
+ throw new IndexOutOfRangeException();
+
+ var value = Unsafe.As(ref _data[Position]);
+ Position += typeSize;
+
+ return value;
+ }
+
+ public T[] ReadArray()
+ where T : unmanaged
+ {
+ var length = Read();
+ var values = new T[length];
+ for (var i = 0; i < length; i++)
+ values[i] = Read();
+ return values;
+ }
+
+ public string ReadString()
+ {
+ var strByteCount = Read();
+
+ if (Position + strByteCount > _data.Length)
+ throw new IndexOutOfRangeException();
+
+ var value = s_utf8NoBom.GetString(_data, Position, strByteCount);
+ Position += strByteCount;
+
+ return value;
+ }
+
+ #region IEnumerable
+
+ public IEnumerator GetEnumerator()
+ {
+ foreach (var b in _data)
+ yield return b;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() =>
+ GetEnumerator();
+
+ #endregion
+ }
+}
diff --git a/src/Neo.Hosting.App/Configuration/NamedPipeClientTransportOptions.cs b/src/Neo.Hosting.App/Configuration/NamedPipeClientTransportOptions.cs
new file mode 100644
index 0000000000..b29a28bc3b
--- /dev/null
+++ b/src/Neo.Hosting.App/Configuration/NamedPipeClientTransportOptions.cs
@@ -0,0 +1,25 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NamedPipeClientTransportOptions.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.Hosting.App.Buffers;
+using System;
+using System.Buffers;
+
+namespace Neo.Hosting.App.Configuration
+{
+ internal sealed class NamedPipeClientTransportOptions
+ {
+ public long MaxReadBufferSize { get; set; } = 1024 * 1024;
+ public long MaxWriteBufferSize { get; set; } = 64 * 1024;
+ public bool CurrentUserOnly { get; set; } = true;
+ internal Func> MemoryPoolFactory { get; set; } = () => new PinnedBlockMemoryPool();
+ }
+}
diff --git a/src/Neo.Hosting.App/Configuration/NamedPipeServerTransportOptions.cs b/src/Neo.Hosting.App/Configuration/NamedPipeServerTransportOptions.cs
new file mode 100644
index 0000000000..a3f00efb59
--- /dev/null
+++ b/src/Neo.Hosting.App/Configuration/NamedPipeServerTransportOptions.cs
@@ -0,0 +1,26 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NamedPipeServerTransportOptions.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.Hosting.App.Buffers;
+using System;
+using System.Buffers;
+
+namespace Neo.Hosting.App.Configuration
+{
+ internal sealed class NamedPipeServerTransportOptions
+ {
+ public int ListenerQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16);
+ public long MaxReadBufferSize { get; set; } = 1024 * 1024;
+ public long MaxWriteBufferSize { get; set; } = 64 * 1024;
+ public bool CurrentUserOnly { get; set; } = true;
+ internal Func> MemoryPoolFactory { get; set; } = () => new PinnedBlockMemoryPool();
+ }
+}
diff --git a/src/Neo.Hosting.App/Configuration/NeoConfigurationSource.cs b/src/Neo.Hosting.App/Configuration/NeoConfigurationSource.cs
new file mode 100644
index 0000000000..d80f17174e
--- /dev/null
+++ b/src/Neo.Hosting.App/Configuration/NeoConfigurationSource.cs
@@ -0,0 +1,23 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoConfigurationSource.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.Extensions.Configuration;
+using Neo.Hosting.App.Providers;
+
+namespace Neo.Hosting.App.Configuration
+{
+ internal class NeoConfigurationSource
+ (IConfigurationSection? configurationSection = null) : IConfigurationSource
+ {
+ public IConfigurationProvider Build(IConfigurationBuilder builder) =>
+ new NeoConfigurationProvider(configurationSection);
+ }
+}
diff --git a/src/Neo.Hosting.App/Configuration/NeoOptions.cs b/src/Neo.Hosting.App/Configuration/NeoOptions.cs
new file mode 100644
index 0000000000..0cda4dc1bb
--- /dev/null
+++ b/src/Neo.Hosting.App/Configuration/NeoOptions.cs
@@ -0,0 +1,110 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoOptions.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.Hosting.App.Helpers;
+using Neo.Hosting.App.Host;
+using Neo.Network.P2P;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security;
+
+namespace Neo.Hosting.App.Configuration
+{
+ internal sealed class NeoOptions
+ {
+ public StorageOptions Storage { get; set; } = new();
+ public P2POptions P2P { get; set; } = new();
+ public ContractOptions Contract { get; set; } = new("0x50ac1c37690cc2cfc594472833cf57505d5f46de");
+ public PluginOptions Plugin { get; set; } = new();
+ public NamedPipeOptions NamedPipe { get; set; } = new();
+ public List Wallets { get; set; } = [];
+ }
+
+ internal sealed class StorageOptions
+ {
+ public class ArchiveSettings
+ {
+ public string Path { get; set; } = AppContext.BaseDirectory;
+ public string FileName { get; set; } = "chain.0.acc";
+ }
+
+ public string Engine { get; set; } = NeoDefaults.StoreProviderName;
+ public string Path { get; set; } = "Data_LevelDB_{0:X2}";
+ public bool Verify { get; set; } = true;
+ public ArchiveSettings Archive { get; set; } = new();
+ }
+
+ internal sealed class P2POptions
+ {
+ public string Listen { get; set; } = "0.0.0.0";
+ public ushort Port { get; set; } = 10333;
+ public int MinDesiredConnections { get; set; } = Peer.DefaultMinDesiredConnections;
+ public int MaxConnections { get; set; } = Peer.DefaultMaxConnections;
+ public int MaxConnectionsPerAddress { get; set; } = 3;
+ }
+
+ internal sealed class ContractOptions
+ (string neoNameService)
+ {
+ private static readonly string s_defualtNameServiceString = "0x50ac1c37690cc2cfc594472833cf57505d5f46de";
+ private static readonly UInt160 s_defaultNameServiceScriptHash = UInt160.Parse(s_defualtNameServiceString);
+
+ private UInt160 _neoNameService = s_defaultNameServiceScriptHash;
+
+ public UInt160 NeoNameService
+ {
+ get => _neoNameService;
+ set => _neoNameService = ParseUtility.TryParseUInt160(neoNameService) ?? s_defaultNameServiceScriptHash;
+ }
+ }
+
+ internal sealed class PluginOptions
+ {
+ public string DownloadUrl { get; set; } = "https://api.github.com/repos/neo-project/neo/releases";
+ public bool Prerelease { get; set; } = false;
+ public Version Version { get; set; } = new(0, 0);
+ }
+
+ internal sealed class NamedPipeOptions
+ {
+ public string Name { get; set; } = default!;
+ }
+
+ internal sealed class WalletOptions
+ (string name, string path, string password, bool isActive)
+ {
+ public string Name { get; set; } = name;
+ public FileInfo Path { get; set; } = new(path);
+ public bool IsActive { get; set; } = isActive;
+
+ public required SecureString Password
+ {
+ get => _encryptedPassword;
+ set
+ {
+ var passwordOptionValue = password;
+
+ unsafe
+ {
+ fixed (char* passwordChars = passwordOptionValue)
+ {
+ var securePasswordString = new SecureString(passwordChars, passwordOptionValue.Length);
+ securePasswordString.MakeReadOnly();
+ _encryptedPassword = value = securePasswordString;
+ }
+ }
+ }
+ }
+
+ private SecureString _encryptedPassword = new();
+ }
+}
diff --git a/src/Neo.Hosting.App/Helpers/ConsoleUtility.cs b/src/Neo.Hosting.App/Helpers/ConsoleUtility.cs
new file mode 100644
index 0000000000..bdbe643ef9
--- /dev/null
+++ b/src/Neo.Hosting.App/Helpers/ConsoleUtility.cs
@@ -0,0 +1,53 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// ConsoleUtility.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 System;
+using System.Runtime.InteropServices;
+
+namespace Neo.Hosting.App
+{
+ internal static partial class ConsoleUtility
+ {
+ private const int STD_OUTPUT_HANDLE = -11;
+ private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
+ private const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
+
+ [LibraryImport("kernel32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static partial bool GetConsoleMode(nint hConsoleHandle, out uint lpMode);
+
+ [LibraryImport("kernel32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static partial bool SetConsoleMode(nint hConsoleHandle, uint dwMode);
+
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ private static partial nint GetStdHandle(int nStdHandle);
+
+ public static void EnableAnsi()
+ {
+ var handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ if (!GetConsoleMode(handle, out var mode))
+ {
+ Console.Error.WriteLine("Failed to get console mode");
+ return;
+ }
+
+ // Enable the virtual terminal processing mode
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
+ if (!SetConsoleMode(handle, mode))
+ {
+ Console.Error.WriteLine("Failed to set console mode");
+ return;
+ }
+ }
+ }
+}
diff --git a/src/Neo.Hosting.App/Helpers/ParseUtility.cs b/src/Neo.Hosting.App/Helpers/ParseUtility.cs
new file mode 100644
index 0000000000..a250a4f370
--- /dev/null
+++ b/src/Neo.Hosting.App/Helpers/ParseUtility.cs
@@ -0,0 +1,24 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// ParseUtility.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.
+
+namespace Neo.Hosting.App.Helpers
+{
+ internal static class ParseUtility
+ {
+ public static UInt160? TryParseUInt160(string? value)
+ {
+ if (string.IsNullOrEmpty(value)) return default;
+ if (UInt160.TryParse(value, out var result))
+ return result;
+ return UInt160.Zero;
+ }
+ }
+}
diff --git a/src/Neo.Hosting.App/Helpers/RegexUtility.cs b/src/Neo.Hosting.App/Helpers/RegexUtility.cs
new file mode 100644
index 0000000000..b394656502
--- /dev/null
+++ b/src/Neo.Hosting.App/Helpers/RegexUtility.cs
@@ -0,0 +1,21 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// RegexUtility.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 System.Text.RegularExpressions;
+
+namespace Neo.Hosting.App
+{
+ internal static partial class RegexUtility
+ {
+ [GeneratedRegex(@"\d+")]
+ public static partial Regex AllNumbersWithOneOrMoreDigits();
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/LoggerCategoryDefaults.cs b/src/Neo.Hosting.App/Host/LoggerCategoryDefaults.cs
new file mode 100644
index 0000000000..ed7f5e6aa0
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/LoggerCategoryDefaults.cs
@@ -0,0 +1,19 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// LoggerCategoryDefaults.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.
+
+namespace Neo.Hosting.App.Host
+{
+ internal static class LoggerCategoryDefaults
+ {
+ public static readonly string RemoteManagement = "Remote.Management";
+ public static readonly string NeoSystemService = $"{typeof(NeoSystem).Name}Service";
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/NeoDefaults.cs b/src/Neo.Hosting.App/Host/NeoDefaults.cs
new file mode 100644
index 0000000000..6da3601936
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/NeoDefaults.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoDefaults.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.
+
+namespace Neo.Hosting.App.Host
+{
+ internal static class NeoDefaults
+ {
+ public static readonly string StoreProviderName = "MemoryStore";
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/NeoEnvironmentVariableDefaults.cs b/src/Neo.Hosting.App/Host/NeoEnvironmentVariableDefaults.cs
new file mode 100644
index 0000000000..1cbc16478d
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/NeoEnvironmentVariableDefaults.cs
@@ -0,0 +1,20 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoEnvironmentVariableDefaults.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.
+
+namespace Neo.Hosting.App.Host
+{
+ internal static class NeoEnvironmentVariableDefaults
+ {
+ public const string PREFIX = "NEO_";
+ public static readonly string PipeName = $"{PREFIX}_NAMEDPIPE__NAME";
+ public static readonly string Environment = $"{PREFIX}ENVIRONMENT";
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/NeoHostingEnvironments.cs b/src/Neo.Hosting.App/Host/NeoHostingEnvironments.cs
new file mode 100644
index 0000000000..4fc25ea874
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/NeoHostingEnvironments.cs
@@ -0,0 +1,25 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoHostingEnvironments.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.
+
+namespace Neo.Hosting.App.Host
+{
+ internal static class NeoHostingEnvironments
+ {
+ public static readonly string LocalNet = "localnet";
+
+ public static readonly string TestNet = "testnet";
+
+ public static readonly string MainNet = "mainnet";
+
+ public static readonly string PrivateNet = "privatenet";
+
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/NeoJsonSectionNameDefaults.cs b/src/Neo.Hosting.App/Host/NeoJsonSectionNameDefaults.cs
new file mode 100644
index 0000000000..14e291a9ae
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/NeoJsonSectionNameDefaults.cs
@@ -0,0 +1,25 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoJsonSectionNameDefaults.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.
+
+namespace Neo.Hosting.App.Host
+{
+ internal class NeoJsonSectionNameDefaults
+ {
+ public static readonly string Application = "ApplicationConfiguration";
+ public static readonly string Archive = "Archive";
+ public static readonly string Contract = "Contract";
+ public static readonly string NamedPipeTransport = "NamedPipeTransport";
+ public static readonly string PeerToPeer = "P2P";
+ public static readonly string Plugin = "Plugin";
+ public static readonly string Remote = "Remote";
+ public static readonly string Storage = "Storage";
+ }
+}
diff --git a/src/Neo.Hosting.App/Host/NeoProtocolSettingsDefaults.cs b/src/Neo.Hosting.App/Host/NeoProtocolSettingsDefaults.cs
new file mode 100644
index 0000000000..81246732be
--- /dev/null
+++ b/src/Neo.Hosting.App/Host/NeoProtocolSettingsDefaults.cs
@@ -0,0 +1,120 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoProtocolSettingsDefaults.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.Cryptography.ECC;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace Neo.Hosting.App.Host
+{
+ internal class NeoProtocolSettingsDefaults
+ {
+ public static ProtocolSettings MainNet = new()
+ {
+ Network = 860833102u,
+ AddressVersion = 53,
+ MillisecondsPerBlock = 15_000u,
+ MaxTransactionsPerBlock = 512u,
+ MemoryPoolMaxTransactions = 50_000,
+ MaxTraceableBlocks = 2_102_400u,
+ InitialGasDistribution = 5_200_000_000_000_000ul,
+ ValidatorsCount = 7,
+ StandbyCommittee = [
+ // Validators
+ ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1),
+ ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", ECCurve.Secp256r1),
+ ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", ECCurve.Secp256r1),
+ ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", ECCurve.Secp256r1),
+ ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", ECCurve.Secp256r1),
+ ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", ECCurve.Secp256r1),
+ ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", ECCurve.Secp256r1),
+ // Other Members
+ ECPoint.Parse("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", ECCurve.Secp256r1),
+ ECPoint.Parse("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", ECCurve.Secp256r1),
+ ECPoint.Parse("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", ECCurve.Secp256r1),
+ ECPoint.Parse("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", ECCurve.Secp256r1),
+ ECPoint.Parse("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", ECCurve.Secp256r1),
+ ECPoint.Parse("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", ECCurve.Secp256r1),
+ ECPoint.Parse("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", ECCurve.Secp256r1),
+ ECPoint.Parse("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", ECCurve.Secp256r1),
+ ECPoint.Parse("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", ECCurve.Secp256r1),
+ ECPoint.Parse("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", ECCurve.Secp256r1),
+ ECPoint.Parse("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", ECCurve.Secp256r1),
+ ECPoint.Parse("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", ECCurve.Secp256r1),
+ ECPoint.Parse("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", ECCurve.Secp256r1),
+ ECPoint.Parse("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a", ECCurve.Secp256r1)
+ ],
+ SeedList = [
+ "seed1.neo.org:10333",
+ "seed2.neo.org:10333",
+ "seed3.neo.org:10333",
+ "seed4.neo.org:10333",
+ "seed5.neo.org:10333"
+ ],
+ Hardforks = new Dictionary()
+ {
+ [Hardfork.HF_Aspidochelone] = 1_730_000u,
+ [Hardfork.HF_Basilisk] = 4_120_000u,
+ [Hardfork.HF_Cockatrice] = 5_450_000u,
+ }.ToImmutableDictionary(),
+ };
+
+ public static ProtocolSettings TestNet = new()
+ {
+ Network = 894710606u,
+ AddressVersion = 53,
+ MillisecondsPerBlock = 15_000u,
+ MaxTransactionsPerBlock = 5_000u,
+ MemoryPoolMaxTransactions = 50_000,
+ MaxTraceableBlocks = 2_102_400u,
+ InitialGasDistribution = 5_200_000_000_000_000ul,
+ ValidatorsCount = 7,
+ StandbyCommittee = [
+ // Validators
+ ECPoint.Parse("023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d", ECCurve.Secp256r1),
+ ECPoint.Parse("03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2", ECCurve.Secp256r1),
+ ECPoint.Parse("02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd", ECCurve.Secp256r1),
+ ECPoint.Parse("03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806", ECCurve.Secp256r1),
+ ECPoint.Parse("02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b", ECCurve.Secp256r1),
+ ECPoint.Parse("0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01", ECCurve.Secp256r1),
+ ECPoint.Parse("030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba", ECCurve.Secp256r1),
+ // Other Members
+ ECPoint.Parse("025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8", ECCurve.Secp256r1),
+ ECPoint.Parse("02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5", ECCurve.Secp256r1),
+ ECPoint.Parse("03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828", ECCurve.Secp256r1),
+ ECPoint.Parse("026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6", ECCurve.Secp256r1),
+ ECPoint.Parse("02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3", ECCurve.Secp256r1),
+ ECPoint.Parse("0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70", ECCurve.Secp256r1),
+ ECPoint.Parse("035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7", ECCurve.Secp256r1),
+ ECPoint.Parse("0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636", ECCurve.Secp256r1),
+ ECPoint.Parse("03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254", ECCurve.Secp256r1),
+ ECPoint.Parse("03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c", ECCurve.Secp256r1),
+ ECPoint.Parse("02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f", ECCurve.Secp256r1),
+ ECPoint.Parse("03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5", ECCurve.Secp256r1),
+ ECPoint.Parse("0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21", ECCurve.Secp256r1),
+ ECPoint.Parse("03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9", ECCurve.Secp256r1)
+ ],
+ SeedList = [
+ "seed1t5.neo.org:20333",
+ "seed2t5.neo.org:20333",
+ "seed3t5.neo.org:20333",
+ "seed4t5.neo.org:20333",
+ "seed5t5.neo.org:20333"
+ ],
+ Hardforks = new Dictionary()
+ {
+ [Hardfork.HF_Aspidochelone] = 210_000u,
+ [Hardfork.HF_Basilisk] = 2_680_000u,
+ [Hardfork.HF_Cockatrice] = 3_967_000u,
+ }.ToImmutableDictionary(),
+ };
+ }
+}
diff --git a/src/Neo.Hosting.App/Neo.Hosting.App.csproj b/src/Neo.Hosting.App/Neo.Hosting.App.csproj
new file mode 100644
index 0000000000..47cf313b67
--- /dev/null
+++ b/src/Neo.Hosting.App/Neo.Hosting.App.csproj
@@ -0,0 +1,62 @@
+
+
+
+ Exe
+ net8.0
+ Neo.Hosting.App
+ neo-cmd
+ enable
+ true
+ ../../bin/$(AssemblyTitle)
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Neo.Hosting.App/Program.cs b/src/Neo.Hosting.App/Program.cs
new file mode 100644
index 0000000000..2633d51c54
--- /dev/null
+++ b/src/Neo.Hosting.App/Program.cs
@@ -0,0 +1,42 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Program.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.Extensions.Hosting.Systemd;
+using Microsoft.Extensions.Hosting.WindowsServices;
+using Neo.Extensions;
+using System;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace Neo.Hosting.App
+{
+ public sealed partial class Program
+ {
+ internal const string DEFAULT_VERSION_STRING = "0.0.0";
+
+ internal static int ApplicationVersionNumber =>
+ AssemblyUtility.GetVersionNumber();
+
+ internal static Version ApplicationVersion =>
+ Assembly.GetExecutingAssembly().GetName().Version ?? new Version("0.0.0");
+
+ internal static bool IsRunningAsService =>
+ (SystemdHelpers.IsSystemdService() || WindowsServiceHelpers.IsWindowsService()) || Environment.UserInteractive == false;
+
+ static Task Main(string[] args)
+ {
+ Console.BackgroundColor = ConsoleColor.Black;
+ Console.Clear();
+
+ return Task.FromResult(-1);
+ }
+ }
+}
diff --git a/src/Neo.Hosting.App/Providers/NeoConfigurationProvider.cs b/src/Neo.Hosting.App/Providers/NeoConfigurationProvider.cs
new file mode 100644
index 0000000000..59005a3664
--- /dev/null
+++ b/src/Neo.Hosting.App/Providers/NeoConfigurationProvider.cs
@@ -0,0 +1,120 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// NeoConfigurationProvider.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 Akka.Util.Internal;
+using Microsoft.Extensions.Configuration;
+using Neo.Hosting.App.Host;
+using Neo.Network.P2P;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Neo.Hosting.App.Providers
+{
+ internal class NeoConfigurationProvider
+ (IConfigurationSection? configurationSection = null) : ConfigurationProvider
+ {
+ public override void Load()
+ {
+ Data = CreateDefaultKeys();
+ Load(Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User));
+ Load(Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process));
+
+ if (configurationSection is not null)
+ Load(configurationSection);
+ }
+
+ internal void Load(IDictionary envVariables)
+ {
+ var e = envVariables.GetEnumerator();
+
+ try
+ {
+ while (e.MoveNext())
+ {
+ var key = (string)e.Entry.Key;
+ var value = (string?)e.Entry.Value;
+
+ AddIfNormalizedKeyMatchesPrefix(Data, Normalize(key), value);
+ }
+ }
+ finally
+ {
+ (e as IDisposable)?.Dispose();
+ }
+ }
+
+ internal void Load(IConfigurationSection section)
+ {
+ var e = section.AsEnumerable().GetEnumerator();
+ var prefix = $"{section.Key}:";
+
+ try
+ {
+ while (e.MoveNext())
+ {
+ var key = e.Current.Key;
+ var value = e.Current.Value;
+
+ if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ Data.AddOrSet(key[prefix.Length..], value);
+ }
+ }
+ finally
+ {
+ (e as IDisposable)?.Dispose();
+ }
+ }
+
+ private static Dictionary CreateDefaultKeys() =>
+ new(StringComparer.OrdinalIgnoreCase)
+ {
+ // Storage
+ ["STORAGE:PATH"] = "Data_LevelDB_{0:X2}",
+ ["STORAGE:ENGINE"] = NeoDefaults.StoreProviderName,
+ ["STORAGE:VERIFY"] = bool.TrueString,
+ ["STORAGE:ARCHIVE:PATH"] = AppContext.BaseDirectory,
+ ["STORAGE:ARCHIVE:FILENAME"] = "chain.{0}.acc",
+
+ // P2P
+ ["P2P:LISTEN"] = "0.0.0.0",
+ ["P2P:PORT"] = "10333",
+ ["P2P:MINDESIREDCONNECTIONS"] = $"{Peer.DefaultMinDesiredConnections}",
+ ["P2P:MAXCONNECTIONS"] = $"{Peer.DefaultMaxConnections}",
+ ["P2P:MAXCONNECTIONSPERADDRESS"] = "3",
+
+ // Contracts
+ ["CONTRACT:NEONAMESERVICE"] = "0x50ac1c37690cc2cfc594472833cf57505d5f46de",
+
+ // Plugin
+ ["PLUGIN:DOWNLOADURL"] = "https://api.github.com/repos/neo-project/neo/releases",
+ ["PLUGIN:PRERELEASE"] = bool.FalseString,
+ ["PLUGIN:VERSION"] = $"{Program.ApplicationVersion.ToString(3)}",
+
+ // Remote
+ ["NAMEDPIPE:NAME"] = $"NEO_SERVICE",
+ };
+
+ private static void AddIfNormalizedKeyMatchesPrefix(IDictionary data, string normalizedKey, string? value)
+ {
+ var normalizedPrefix1 = NeoEnvironmentVariableDefaults.PREFIX;
+ var normalizedPrefix2 = $"NEO:";
+
+ if (normalizedKey.StartsWith(normalizedPrefix1, StringComparison.OrdinalIgnoreCase))
+ data[normalizedKey[normalizedPrefix1.Length..]] = value;
+ else if (normalizedKey.StartsWith(normalizedPrefix2, StringComparison.OrdinalIgnoreCase))
+ data[normalizedKey[normalizedPrefix2.Length..]] = value;
+ }
+
+ private static string Normalize(string key) =>
+ key.Replace("__", ConfigurationPath.KeyDelimiter);
+ }
+}
diff --git a/src/Neo.Hosting.App/config.localnet.json b/src/Neo.Hosting.App/config.localnet.json
new file mode 100644
index 0000000000..ec91c176bb
--- /dev/null
+++ b/src/Neo.Hosting.App/config.localnet.json
@@ -0,0 +1,83 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information"
+ },
+ "NeoError": {
+ "LogLevel": {
+ "Default": "Error"
+ }
+ }
+ },
+ "ApplicationConfiguration": {
+ "Storage": {
+ "Engine": "MemoryStore",
+ "Verify": false,
+ "Archive": {
+ "FileName": "chain.{0}.acc"
+ }
+ },
+ "P2P": {
+ "Listen": "127.0.0.1",
+ "Port": 226
+ },
+ "Contract": {
+ "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
+ },
+ "NamedPipe": {
+ "Name": "HelloPipe"
+ },
+ "Wallets": [
+ {
+ "Name": "MyTestWallet",
+ "Path": "./wallets/hello.json",
+ "Password": "hello",
+ "IsActive": true
+ }
+ ]
+ },
+ "ProtocolConfiguration": {
+ "Network": 860833102,
+ "AddressVersion": 53,
+ "MillisecondsPerBlock": 15000,
+ "MaxTransactionsPerBlock": 512,
+ "MemoryPoolMaxTransactions": 50000,
+ "MaxTraceableBlocks": 2102400,
+ "Hardforks": {
+ "HF_Aspidochelone": 1730000,
+ "HF_Basilisk": 4120000
+ },
+ "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"
+ ]
+ }
+}
diff --git a/src/Neo.Hosting.App/config.mainnet.json b/src/Neo.Hosting.App/config.mainnet.json
new file mode 100644
index 0000000000..5db07486e7
--- /dev/null
+++ b/src/Neo.Hosting.App/config.mainnet.json
@@ -0,0 +1,54 @@
+{
+ "ApplicationConfiguration": {
+ "Storage": {
+ "Engine": "LevelDBStore"
+ },
+ "Contracts": {
+ "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
+ }
+ },
+ "ProtocolConfiguration": {
+ "Network": 860833102,
+ "AddressVersion": 53,
+ "MillisecondsPerBlock": 15000,
+ "MaxTransactionsPerBlock": 512,
+ "MemoryPoolMaxTransactions": 50000,
+ "MaxTraceableBlocks": 2102400,
+ "Hardforks": {
+ "HF_Aspidochelone": 1730000,
+ "HF_Basilisk": 4120000
+ },
+ "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"
+ ]
+ }
+}
diff --git a/src/Neo.Hosting.App/config.testnet.json b/src/Neo.Hosting.App/config.testnet.json
new file mode 100644
index 0000000000..4bef69b0d4
--- /dev/null
+++ b/src/Neo.Hosting.App/config.testnet.json
@@ -0,0 +1,57 @@
+{
+ "ApplicationConfiguration": {
+ "Storage": {
+ "Engine": "LevelDBStore"
+ },
+ "P2P": {
+ "Port": 20333
+ },
+ "Contracts": {
+ "NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
+ }
+ },
+ "ProtocolConfiguration": {
+ "Network": 894710606,
+ "AddressVersion": 53,
+ "MillisecondsPerBlock": 15000,
+ "MaxTransactionsPerBlock": 5000,
+ "MemoryPoolMaxTransactions": 50000,
+ "MaxTraceableBlocks": 2102400,
+ "Hardforks": {
+ "HF_Aspidochelone": 210000,
+ "HF_Basilisk": 2680000
+ },
+ "InitialGasDistribution": 5200000000000000,
+ "ValidatorsCount": 7,
+ "StandbyCommittee": [
+ "023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d",
+ "03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2",
+ "02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd",
+ "03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806",
+ "02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b",
+ "0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01",
+ "030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba",
+ "025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8",
+ "02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5",
+ "03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828",
+ "026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6",
+ "02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3",
+ "0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70",
+ "035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7",
+ "0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636",
+ "03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254",
+ "03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c",
+ "02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f",
+ "03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5",
+ "0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21",
+ "03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9"
+ ],
+ "SeedList": [
+ "seed1t5.neo.org:20333",
+ "seed2t5.neo.org:20333",
+ "seed3t5.neo.org:20333",
+ "seed4t5.neo.org:20333",
+ "seed5t5.neo.org:20333"
+ ]
+ }
+}
diff --git a/src/Neo/Cryptography/Crc32.cs b/src/Neo/Cryptography/Crc32.cs
new file mode 100644
index 0000000000..15af3bad88
--- /dev/null
+++ b/src/Neo/Cryptography/Crc32.cs
@@ -0,0 +1,116 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Crc32.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 System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+
+namespace Neo.Cryptography
+{
+ public sealed class Crc32 : HashAlgorithm
+ {
+ public static readonly uint DefaultPolynomial = 0xedb88320u;
+ public static readonly uint DefaultSeed = 0xffffffffu;
+
+ private static uint[] s_defaultTable;
+
+ public override int HashSize => 32;
+
+ private readonly uint _seed;
+ private readonly uint[] _table;
+ private uint _hash;
+
+ public Crc32() : this(DefaultPolynomial, DefaultSeed)
+ {
+
+ }
+
+ public Crc32(
+ uint polynomial,
+ uint seed)
+ {
+ if (BitConverter.IsLittleEndian)
+ throw new PlatformNotSupportedException("Not supported on Big Endian processors");
+
+ _table = InitializeTable(polynomial);
+ _seed = seed;
+ }
+
+ public override void Initialize()
+ {
+ _hash = _seed;
+ }
+
+ protected override void HashCore(byte[] array, int ibStart, int cbSize)
+ {
+ _hash = CalculateHash(_table, _hash, array, ibStart, cbSize);
+ }
+
+ protected override byte[] HashFinal()
+ {
+ var hashBuffer = Crc32.UInt32ToBigEndianBytes(~_hash);
+ HashValue = hashBuffer;
+ return hashBuffer;
+ }
+
+ public static uint Compute(byte[] buffer) =>
+ Compute(DefaultSeed, buffer);
+
+ public static uint Compute(uint seed, byte[] buffer) =>
+ Compute(DefaultPolynomial, seed, buffer);
+
+ public static uint Compute(uint polynomial, uint seed, byte[] buffer) =>
+ ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
+
+ private static uint[] InitializeTable(uint polynomial)
+ {
+ if (polynomial == DefaultPolynomial && s_defaultTable != null)
+ return s_defaultTable;
+
+ var createTable = new uint[256];
+ for (var i = 0u; i < 256u; i++)
+ {
+ var entry = i;
+ for (var j = 0; j < 8; j++)
+ {
+ if ((entry & 1) == 1)
+ entry = (entry >> 1) ^ polynomial;
+ else
+ entry >>= 1;
+ }
+ createTable[i] = entry;
+ }
+
+ if (polynomial == DefaultPolynomial)
+ s_defaultTable = createTable;
+
+ return createTable;
+ }
+
+ private static uint CalculateHash(uint[] table, uint seed, IList buffer, int start, int size)
+ {
+ var hash = seed;
+ for (var i = start; i < start + size; i++)
+ hash = (hash >> 8) ^ table[buffer[i] ^ hash & 0xff];
+ return hash;
+ }
+
+ private static byte[] UInt32ToBigEndianBytes(uint value)
+ {
+ var result = BitConverter.GetBytes(value);
+
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(result);
+
+ return result;
+ }
+ }
+}
diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj
index 835224eddd..d38e02e2ce 100644
--- a/src/Neo/Neo.csproj
+++ b/src/Neo/Neo.csproj
@@ -29,7 +29,9 @@
+
+
diff --git a/tests/Neo.Hosting.App.Tests/Neo.Hosting.App.Tests.csproj b/tests/Neo.Hosting.App.Tests/Neo.Hosting.App.Tests.csproj
new file mode 100644
index 0000000000..38de1a2849
--- /dev/null
+++ b/tests/Neo.Hosting.App.Tests/Neo.Hosting.App.Tests.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net8.0
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Neo.Hosting.App.Tests/UTHelpers/DataCases/TestMemberDataCases.cs b/tests/Neo.Hosting.App.Tests/UTHelpers/DataCases/TestMemberDataCases.cs
new file mode 100644
index 0000000000..69d0903a23
--- /dev/null
+++ b/tests/Neo.Hosting.App.Tests/UTHelpers/DataCases/TestMemberDataCases.cs
@@ -0,0 +1,43 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// TestMemberDataCases.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;
+using Neo.Hosting;
+using Neo.Hosting.App;
+using Neo.Hosting.App.Tests;
+using Neo.Hosting.App.Tests.UTHelpers;
+using Neo.Hosting.App.Tests.UTHelpers.DataCases;
+
+namespace Neo.Hosting.App.Tests.UTHelpers.DataCases
+{
+ internal static class TestMemberDataCases
+ {
+ public static TheoryData