From 906b3f25f694be513711e481e7a14bdb49987cb6 Mon Sep 17 00:00:00 2001
From: Anna Shaleva
Date: Tue, 5 Sep 2023 08:47:40 +0300
Subject: [PATCH 01/30] DBFTPlugin: adapt Conflicts attribute verification
(#802)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* DBFTPlugin: adapt Conflicts attribute verification
Depends on https://github.com/neo-project/neo/pull/2818, see the
https://github.com/neo-project/neo/pull/2818#issuecomment-1548814976.
Signed-off-by: Anna Shaleva
* Fix Conflicts attribute verification
Fetch the update from core: https://github.com/neo-project/neo/pull/2818#issuecomment-1562554901.
* Fetch on-chain conflict signer fix from core
Use new API from https://github.com/neo-project/neo/pull/2818/commits/ee7333f1696b66a59a742abcf7d6615528db9724.
---------
Signed-off-by: Anna Shaleva
Co-authored-by: Vitor Nazário Coelho
Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
Co-authored-by: Jimmy
---
.../Consensus/ConsensusService.OnMessage.cs | 13 ++++++++
src/DBFTPlugin/Consensus/ConsensusService.cs | 33 ++++++++++++++++++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs b/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs
index df358fdc4..cac84a8d4 100644
--- a/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs
+++ b/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs
@@ -127,13 +127,26 @@ private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest
{
if (mempoolVerified.TryGetValue(hash, out Transaction tx))
{
+ if (NativeContract.Ledger.ContainsConflictHash(context.Snapshot, hash, tx.Signers.Select(s => s.Account)))
+ {
+ Log($"Invalid request: transaction has on-chain conflict", LogLevel.Warning);
+ return;
+ }
+
if (!AddTransaction(tx, false))
return;
}
else
{
if (neoSystem.MemPool.TryGetValue(hash, out tx))
+ {
+ if (NativeContract.Ledger.ContainsConflictHash(context.Snapshot, hash, tx.Signers.Select(s => s.Account)))
+ {
+ Log($"Invalid request: transaction has on-chain conflict", LogLevel.Warning);
+ return;
+ }
unverified.Add(tx);
+ }
}
}
foreach (Transaction tx in unverified)
diff --git a/src/DBFTPlugin/Consensus/ConsensusService.cs b/src/DBFTPlugin/Consensus/ConsensusService.cs
index e50ab42d2..dc8f66ff1 100644
--- a/src/DBFTPlugin/Consensus/ConsensusService.cs
+++ b/src/DBFTPlugin/Consensus/ConsensusService.cs
@@ -257,7 +257,38 @@ private bool AddTransaction(Transaction tx, bool verify)
{
if (verify)
{
- VerifyResult result = tx.Verify(neoSystem.Settings, context.Snapshot, context.VerificationContext);
+ // At this step we're sure that there's no on-chain transaction that conflicts with
+ // the provided tx because of the previous Blockchain's OnReceive check. Thus, we only
+ // need to check that current context doesn't contain conflicting transactions.
+ VerifyResult result;
+
+ // Firstly, check whether tx has Conlicts attribute with the hash of one of the context's transactions.
+ foreach (var h in tx.GetAttributes().Select(attr => attr.Hash))
+ {
+ if (context.TransactionHashes.Contains(h))
+ {
+ result = VerifyResult.HasConflicts;
+ Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
+ RequestChangeView(ChangeViewReason.TxInvalid);
+ return false;
+ }
+ }
+ // After that, check whether context's transactions have Conflicts attribute with tx's hash.
+ foreach (var pooledTx in context.Transactions.Values)
+ {
+ if (pooledTx.GetAttributes().Select(attr => attr.Hash).Contains(tx.Hash))
+ {
+ result = VerifyResult.HasConflicts;
+ Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
+ RequestChangeView(ChangeViewReason.TxInvalid);
+ return false;
+ }
+ }
+
+ // We've ensured that there's no conlicting transactions in the context, thus, can safely provide an empty conflicting list
+ // for futher verification.
+ var conflictingTxs = new List();
+ result = tx.Verify(neoSystem.Settings, context.Snapshot, context.VerificationContext, conflictingTxs);
if (result != VerifyResult.Succeed)
{
Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
From 1416bb986fa44ed258a7b314b3cff55c9f93af1f Mon Sep 17 00:00:00 2001
From: Shargon
Date: Tue, 5 Sep 2023 12:18:52 +0200
Subject: [PATCH 02/30] Fix 3.6 (#816)
* Fix 3.6
* Update nuget
* Fix version
---
src/DBFTPlugin/Consensus/ConsensusService.cs | 2 +-
src/Directory.Build.props | 4 ++--
src/RocksDBStore/RocksDBStore.csproj | 2 +-
src/SQLiteWallet/SQLiteWallet.csproj | 2 +-
.../Neo.Cryptography.MPTTrie.Tests.csproj | 6 ++++++
.../Neo.Network.RPC.Tests.csproj | 12 +++++++++---
.../Neo.Plugins.OracleService.Tests.csproj | 10 ++++++++--
.../Neo.Plugins.RpcServer.Tests.csproj | 8 +++++++-
.../Neo.Plugins.Storage.Tests.csproj | 6 ++++++
9 files changed, 41 insertions(+), 11 deletions(-)
diff --git a/src/DBFTPlugin/Consensus/ConsensusService.cs b/src/DBFTPlugin/Consensus/ConsensusService.cs
index dc8f66ff1..dcf17f84f 100644
--- a/src/DBFTPlugin/Consensus/ConsensusService.cs
+++ b/src/DBFTPlugin/Consensus/ConsensusService.cs
@@ -45,7 +45,7 @@ private class Timer { public uint Height; public byte ViewNumber; }
/// This will be cleared every block (so it will not grow out of control, but is used to prevent repeatedly
/// responding to the same message.
///
- private readonly HashSet knownHashes = new HashSet();
+ private readonly HashSet knownHashes = new();
///
/// This variable is only true during OnRecoveryMessageReceived
///
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index edf759437..cc6740f46 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,7 +2,7 @@
- 3.5.0
+ 3.6.0net7.0Neo.PluginsThe Neo Project
@@ -21,7 +21,7 @@
-
+
diff --git a/src/RocksDBStore/RocksDBStore.csproj b/src/RocksDBStore/RocksDBStore.csproj
index 4ab704d0e..d098bb8d3 100644
--- a/src/RocksDBStore/RocksDBStore.csproj
+++ b/src/RocksDBStore/RocksDBStore.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/SQLiteWallet/SQLiteWallet.csproj b/src/SQLiteWallet/SQLiteWallet.csproj
index 49b8de1e8..e1d77a3f1 100644
--- a/src/SQLiteWallet/SQLiteWallet.csproj
+++ b/src/SQLiteWallet/SQLiteWallet.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj b/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj
index 1b75c6025..aec30cd98 100644
--- a/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj
+++ b/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj
@@ -7,4 +7,10 @@
+
+
+
+
+
+
diff --git a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj
index 03a168e92..9815f51e9 100644
--- a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj
+++ b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj
@@ -5,9 +5,9 @@
-
-
-
+
+
+ allruntime; build; native; contentfiles; analyzers; buildtransitive
@@ -23,4 +23,10 @@
+
+
+
+
+
+
diff --git a/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj b/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj
index ca84e4303..f2a836e3c 100644
--- a/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj
+++ b/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj
@@ -6,12 +6,18 @@
-
-
+
+
+
+
+
+
+
+
diff --git a/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj b/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj
index 422b0ae28..6105eaf11 100644
--- a/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj
+++ b/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj
@@ -10,7 +10,13 @@
-
+
+
+
+
+
+
+
diff --git a/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj b/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj
index c6798b71a..2cdcc8345 100644
--- a/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj
+++ b/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj
@@ -9,4 +9,10 @@
+
+
+
+
+
+
From 09c2879958a916e0867fc78c64a04edfabe6935f Mon Sep 17 00:00:00 2001
From: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
Date: Tue, 5 Sep 2023 19:02:04 +0800
Subject: [PATCH 03/30] remove Travis logo (#818)
---
README.md | 3 ---
1 file changed, 3 deletions(-)
diff --git a/README.md b/README.md
index 0919ba2fd..91e0aa3b4 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,6 @@
-
-
-
From 3e0efe6583f14f15e0c41df7b3302f6fc6160d5e Mon Sep 17 00:00:00 2001
From: Shine Li
Date: Wed, 13 Sep 2023 18:34:03 +0800
Subject: [PATCH 04/30] init (#821)
---
src/RpcServer/RpcServer.SmartContract.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/RpcServer/RpcServer.SmartContract.cs b/src/RpcServer/RpcServer.SmartContract.cs
index cf22c2780..1a7c24c17 100644
--- a/src/RpcServer/RpcServer.SmartContract.cs
+++ b/src/RpcServer/RpcServer.SmartContract.cs
@@ -23,6 +23,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using Array = System.Array;
namespace Neo.Plugins
{
@@ -169,9 +170,9 @@ private static Signer[] SignersFromJson(JArray _params, ProtocolSettings setting
{
Account = AddressToScriptHash(u["account"].AsString(), settings.AddressVersion),
Scopes = (WitnessScope)Enum.Parse(typeof(WitnessScope), u["scopes"]?.AsString()),
- AllowedContracts = ((JArray)u["allowedcontracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray(),
- AllowedGroups = ((JArray)u["allowedgroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray(),
- Rules = ((JArray)u["rules"])?.Select(r => WitnessRule.FromJson((JObject)r)).ToArray(),
+ AllowedContracts = ((JArray)u["allowedcontracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray() ?? Array.Empty(),
+ AllowedGroups = ((JArray)u["allowedgroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray() ?? Array.Empty(),
+ Rules = ((JArray)u["rules"])?.Select(r => WitnessRule.FromJson((JObject)r)).ToArray() ?? Array.Empty(),
}).ToArray();
// Validate format
From 63e3ec3dc4092ea83f010b1fe9704a70129fbcda Mon Sep 17 00:00:00 2001
From: edge
Date: Fri, 15 Sep 2023 11:16:08 +0100
Subject: [PATCH 05/30] add hardfork settings to getversion (#823)
---
src/RpcServer/RpcServer.Node.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/RpcServer/RpcServer.Node.cs b/src/RpcServer/RpcServer.Node.cs
index 7c8dfeaa8..4c6a0109a 100644
--- a/src/RpcServer/RpcServer.Node.cs
+++ b/src/RpcServer/RpcServer.Node.cs
@@ -82,6 +82,13 @@ protected virtual JToken GetVersion(JArray _params)
json["protocol"]["maxtransactionsperblock"] = system.Settings.MaxTransactionsPerBlock;
json["protocol"]["memorypoolmaxtransactions"] = system.Settings.MemoryPoolMaxTransactions;
json["protocol"]["initialgasdistribution"] = system.Settings.InitialGasDistribution;
+ json["protocol"]["hardforks"] = new JArray(system.Settings.Hardforks.Select(hf =>
+ {
+ JObject forkJson = new();
+ forkJson["name"] = hf.Key;
+ forkJson["blockheight"] = hf.Value;
+ return forkJson;
+ }));
return json;
}
From d36dcbbad6aca803eb49b562ba791843e51d5b8d Mon Sep 17 00:00:00 2001
From: Shine Li
Date: Mon, 25 Sep 2023 11:19:51 +0800
Subject: [PATCH 06/30] Fix RpcClient TxConflict (#830)
* init
* support not valid before
---
src/RpcClient/Utility.cs | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/RpcClient/Utility.cs b/src/RpcClient/Utility.cs
index 159af4046..24b7b8551 100644
--- a/src/RpcClient/Utility.cs
+++ b/src/RpcClient/Utility.cs
@@ -197,6 +197,14 @@ public static TransactionAttribute TransactionAttributeFromJson(JObject json)
Code = Enum.Parse(json["code"].AsString()),
Result = Convert.FromBase64String(json["result"].AsString()),
},
+ TransactionAttributeType.NotValidBefore => new NotValidBefore()
+ {
+ Height = (uint)json["height"].AsNumber(),
+ },
+ TransactionAttributeType.Conflicts => new Conflicts()
+ {
+ Hash = UInt256.Parse(json["hash"].AsString())
+ },
_ => throw new FormatException(),
};
}
From 7b4145caa64c785b322d9169bdc34e5db0d1980b Mon Sep 17 00:00:00 2001
From: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
Date: Mon, 25 Sep 2023 23:08:35 +0800
Subject: [PATCH 07/30] v3.6.1 (#831)
---
src/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index cc6740f46..717700b72 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,7 +2,7 @@
- 3.6.0
+ 3.6.1net7.0Neo.PluginsThe Neo Project
From 6ead78007ba6003eb050f7c7bffeb4e2476f348e Mon Sep 17 00:00:00 2001
From: Christopher Schuchardt
Date: Tue, 26 Sep 2023 07:46:58 -0400
Subject: [PATCH 08/30] Added hardfork to RpcClient (#825)
* Added hardfork to RpcClient
* fixed unit test for getversionasync
---
src/RpcClient/Models/RpcVersion.cs | 10 ++++++++++
tests/Neo.Network.RPC.Tests/RpcTestCases.json | 8 +++++++-
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/RpcClient/Models/RpcVersion.cs b/src/RpcClient/Models/RpcVersion.cs
index d978c2991..2b02dd714 100644
--- a/src/RpcClient/Models/RpcVersion.cs
+++ b/src/RpcClient/Models/RpcVersion.cs
@@ -9,6 +9,9 @@
// modifications are permitted.
using Neo.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
namespace Neo.Network.RPC.Models
{
@@ -25,6 +28,7 @@ public class RpcProtocol
public uint MaxTransactionsPerBlock { get; set; }
public int MemoryPoolMaxTransactions { get; set; }
public ulong InitialGasDistribution { get; set; }
+ public IReadOnlyDictionary Hardforks { get; set; }
public JObject ToJson()
{
@@ -38,6 +42,11 @@ public JObject ToJson()
json["maxtransactionsperblock"] = MaxTransactionsPerBlock;
json["memorypoolmaxtransactions"] = MemoryPoolMaxTransactions;
json["initialgasdistribution"] = InitialGasDistribution;
+ json["hardforks"] = new JArray(Hardforks.Select(s => new JObject()
+ {
+ ["name"] = s.Key,
+ ["blockheight"] = s.Value,
+ }));
return json;
}
@@ -54,6 +63,7 @@ public static RpcProtocol FromJson(JObject json)
MaxTransactionsPerBlock = (uint)json["maxtransactionsperblock"].AsNumber(),
MemoryPoolMaxTransactions = (int)json["memorypoolmaxtransactions"].AsNumber(),
InitialGasDistribution = (ulong)json["initialgasdistribution"].AsNumber(),
+ Hardforks = new Dictionary(((JArray)json["hardforks"]).Select(s => new KeyValuePair(Enum.Parse(s["name"].AsString()), (uint)s["blockheight"].AsNumber()))),
};
}
}
diff --git a/tests/Neo.Network.RPC.Tests/RpcTestCases.json b/tests/Neo.Network.RPC.Tests/RpcTestCases.json
index f8b1e59b8..b65d4da18 100644
--- a/tests/Neo.Network.RPC.Tests/RpcTestCases.json
+++ b/tests/Neo.Network.RPC.Tests/RpcTestCases.json
@@ -2491,7 +2491,13 @@
"addressversion": 0,
"maxtransactionsperblock": 0,
"memorypoolmaxtransactions": 0,
- "initialgasdistribution": 0
+ "initialgasdistribution": 0,
+ "hardforks": [
+ {
+ "name": "HF_Aspidochelone",
+ "blockheight": 0
+ }
+ ]
}
}
}
From 12746bf301c86729f7724802409df041fd7c9bcf Mon Sep 17 00:00:00 2001
From: Anna Shaleva
Date: Sun, 15 Oct 2023 08:00:54 +0300
Subject: [PATCH 09/30] RpcServer, RpcClient: strip HF_ prefix from
`getversion` response (#838)
* RpcServer, RpcClient: strip HF_ prefix from `getversion` response
It's nice to have clear hardfork ame without hardfork prefix so that
the resulting hardfork JSON-ized name can be applicable by both C#
and NeoGo nodes.
Signed-off-by: Anna Shaleva
* tests: adjust `getversion` test response
Strip `HF_` prefix from the hardforks response.
Fix @cschuchardt88's review: https://github.com/neo-project/neo-modules/pull/838#discussion_r1357709898.
Signed-off-by: Anna Shaleva
* Check `HF_` prefix before cutting/adding it to JSON-ized hardforks
Fix @shargon's review: https://github.com/neo-project/neo-modules/pull/838#pullrequestreview-1675991434.
Signed-off-by: Anna Shaleva
---------
Signed-off-by: Anna Shaleva
---
src/RpcClient/Models/RpcVersion.cs | 15 +++++++++++++--
src/RpcServer/RpcServer.Node.cs | 8 +++++++-
tests/Neo.Network.RPC.Tests/RpcTestCases.json | 2 +-
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/src/RpcClient/Models/RpcVersion.cs b/src/RpcClient/Models/RpcVersion.cs
index 2b02dd714..286006fb0 100644
--- a/src/RpcClient/Models/RpcVersion.cs
+++ b/src/RpcClient/Models/RpcVersion.cs
@@ -44,7 +44,8 @@ public JObject ToJson()
json["initialgasdistribution"] = InitialGasDistribution;
json["hardforks"] = new JArray(Hardforks.Select(s => new JObject()
{
- ["name"] = s.Key,
+ // Strip HF_ prefix.
+ ["name"] = StripPrefix(s.Key.ToString(), "HF_"),
["blockheight"] = s.Value,
}));
return json;
@@ -63,9 +64,19 @@ public static RpcProtocol FromJson(JObject json)
MaxTransactionsPerBlock = (uint)json["maxtransactionsperblock"].AsNumber(),
MemoryPoolMaxTransactions = (int)json["memorypoolmaxtransactions"].AsNumber(),
InitialGasDistribution = (ulong)json["initialgasdistribution"].AsNumber(),
- Hardforks = new Dictionary(((JArray)json["hardforks"]).Select(s => new KeyValuePair(Enum.Parse(s["name"].AsString()), (uint)s["blockheight"].AsNumber()))),
+ Hardforks = new Dictionary(((JArray)json["hardforks"]).Select(s =>
+ {
+ var name = s["name"].AsString();
+ // Add HF_ prefix to the hardfork response for proper Hardfork enum parsing.
+ return new KeyValuePair(Enum.Parse(name.StartsWith("HF_") ? name : $"HF_{name}"), (uint)s["blockheight"].AsNumber());
+ })),
};
}
+
+ private static string StripPrefix(string s, string prefix)
+ {
+ return s.StartsWith(prefix) ? s.Substring(prefix.Length) : s;
+ }
}
public int TcpPort { get; set; }
diff --git a/src/RpcServer/RpcServer.Node.cs b/src/RpcServer/RpcServer.Node.cs
index 4c6a0109a..519f85498 100644
--- a/src/RpcServer/RpcServer.Node.cs
+++ b/src/RpcServer/RpcServer.Node.cs
@@ -85,13 +85,19 @@ protected virtual JToken GetVersion(JArray _params)
json["protocol"]["hardforks"] = new JArray(system.Settings.Hardforks.Select(hf =>
{
JObject forkJson = new();
- forkJson["name"] = hf.Key;
+ // Strip "HF_" prefix.
+ forkJson["name"] = StripPrefix(hf.Key.ToString(), "HF_");
forkJson["blockheight"] = hf.Value;
return forkJson;
}));
return json;
}
+ private static string StripPrefix(string s, string prefix)
+ {
+ return s.StartsWith(prefix) ? s.Substring(prefix.Length) : s;
+ }
+
[RpcMethod]
protected virtual JToken SendRawTransaction(JArray _params)
{
diff --git a/tests/Neo.Network.RPC.Tests/RpcTestCases.json b/tests/Neo.Network.RPC.Tests/RpcTestCases.json
index b65d4da18..903d48307 100644
--- a/tests/Neo.Network.RPC.Tests/RpcTestCases.json
+++ b/tests/Neo.Network.RPC.Tests/RpcTestCases.json
@@ -2494,7 +2494,7 @@
"initialgasdistribution": 0,
"hardforks": [
{
- "name": "HF_Aspidochelone",
+ "name": "Aspidochelone",
"blockheight": 0
}
]
From 4f55458bda29a0e80258add2a3768dcc014ab24d Mon Sep 17 00:00:00 2001
From: Shine Li
Date: Mon, 23 Oct 2023 14:55:42 +0800
Subject: [PATCH 10/30] Add Cancel Tx RpcApi (#837)
* init
* Update src/RpcServer/RpcServer.Wallet.cs
Co-authored-by: Christopher Schuchardt
* Update src/RpcServer/RpcServer.Wallet.cs
Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
* Update src/RpcServer/RpcServer.Wallet.cs
Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
* Clean empty lines
* Clean empty lines
* Update src/RpcServer/RpcServer.Wallet.cs
---------
Co-authored-by: Christopher Schuchardt
Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
Co-authored-by: Shargon
Co-authored-by: Jimmy
---
src/RpcClient/RpcClient.cs | 10 ++++++
src/RpcServer/RpcServer.Wallet.cs | 51 +++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/src/RpcClient/RpcClient.cs b/src/RpcClient/RpcClient.cs
index 957025f1a..6291ce044 100644
--- a/src/RpcClient/RpcClient.cs
+++ b/src/RpcClient/RpcClient.cs
@@ -645,6 +645,16 @@ public async Task SendToAddressAsync(string assetId, string address, st
.ConfigureAwait(false);
}
+ ///
+ /// Cancel Tx.
+ ///
+ /// This function returns Signed Transaction JSON if successful, ContractParametersContext JSON if signing failed.
+ public async Task CancelTransactionAsync(UInt256 txId, string[] signers, string extraFee)
+ {
+ JToken[] parameters = signers.Select(s => (JString)s.AsScriptHash()).ToArray();
+ return (JObject)await RpcSendAsync(GetRpcName(), txId.ToString(), new JArray(parameters), extraFee).ConfigureAwait(false);
+ }
+
#endregion Wallet
#region Plugins
diff --git a/src/RpcServer/RpcServer.Wallet.cs b/src/RpcServer/RpcServer.Wallet.cs
index 7ced0421a..097dafcd3 100644
--- a/src/RpcServer/RpcServer.Wallet.cs
+++ b/src/RpcServer/RpcServer.Wallet.cs
@@ -319,6 +319,57 @@ protected virtual JToken SendToAddress(JArray _params)
return SignAndRelay(snapshot, tx);
}
+ [RpcMethod]
+ protected virtual JToken CancelTransaction(JArray _params)
+ {
+ CheckWallet();
+ var txid = UInt256.Parse(_params[0].AsString());
+ TransactionState state = NativeContract.Ledger.GetTransactionState(system.StoreView, txid);
+ if (state != null)
+ {
+ throw new RpcException(32700, "This tx is already confirmed, can't be cancelled.");
+ }
+
+ var conflict = new TransactionAttribute[] { new Conflicts() { Hash = txid } };
+ Signer[] signers = _params.Count >= 2 ? ((JArray)_params[1]).Select(j => new Signer() { Account = AddressToScriptHash(j.AsString(), system.Settings.AddressVersion), Scopes = WitnessScope.None }).ToArray() : Array.Empty();
+ if (!signers.Any())
+ {
+ throw new RpcException(32701, "no signers");
+ }
+
+ Transaction tx = new Transaction
+ {
+ Signers = signers,
+ Attributes = conflict,
+ Witnesses = Array.Empty(),
+ };
+
+ try
+ {
+ tx = wallet.MakeTransaction(system.StoreView, new[] { (byte)OpCode.RET }, signers[0].Account, signers, conflict);
+ }
+ catch (InvalidOperationException e)
+ {
+ throw new RpcException(-500, GetExceptionMessage(e));
+ }
+
+ if (system.MemPool.TryGetValue(txid, out Transaction conflictTx))
+ {
+ tx.NetworkFee = Math.Max(tx.NetworkFee, conflictTx.NetworkFee) + 1;
+ }
+ else if (_params.Count >= 3)
+ {
+ var extraFee = _params[2].AsString();
+ AssetDescriptor descriptor = new(system.StoreView, system.Settings, NativeContract.GAS.Hash);
+ if (!BigDecimal.TryParse(extraFee, descriptor.Decimals, out BigDecimal decimalExtraFee) || decimalExtraFee.Sign <= 0)
+ {
+ throw new RpcException(32702, "Incorrect Amount Format");
+ }
+ tx.NetworkFee += (long)decimalExtraFee.Value;
+ };
+ return SignAndRelay(system.StoreView, tx);
+ }
+
[RpcMethod]
protected virtual JToken InvokeContractVerify(JArray _params)
{
From 35993a4dbcf78e889c35c00a0cbb019cafe42e3d Mon Sep 17 00:00:00 2001
From: Christopher Schuchardt
Date: Tue, 7 Nov 2023 23:14:14 -0500
Subject: [PATCH 11/30] RpcClient: Check for NEP17 Transfer (#833)
* added issue 775
* changed with output
* changed error to match error of spec
* fixed exception output
* fixed issue 775
* Added `.ConfigureAwait(false)` to `InvokeScriptAsync`
* Removed unused namespaces and fixed formatting
* fixed tests for CreateTransferTxAsync
* Code comment removed for invalid param
* Use Assert in script for nep-17 transfers
Revert "v3.6.1 (#831)"
This reverts commit 7b4145caa64c785b322d9169bdc34e5db0d1980b.
* fixed tests
* Update Directory.Build.props
guess i went back to much
* Add argument
---------
Co-authored-by: Shargon
Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
---
src/RpcClient/Nep17API.cs | 14 ++++++++---
src/RpcClient/WalletAPI.cs | 28 +++++++++++++--------
tests/Neo.Network.RPC.Tests/UT_Nep17API.cs | 12 ++++++---
tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs | 23 +++++++++++------
4 files changed, 51 insertions(+), 26 deletions(-)
diff --git a/src/RpcClient/Nep17API.cs b/src/RpcClient/Nep17API.cs
index 54fde2e6e..f0aa19dfd 100644
--- a/src/RpcClient/Nep17API.cs
+++ b/src/RpcClient/Nep17API.cs
@@ -132,15 +132,18 @@ public async Task GetTokenInfoAsync(string contractHash)
/// to account script hash
/// transfer amount
/// onPayment data
+ /// Add assert at the end of the script
///
- public async Task CreateTransferTxAsync(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, object data = null)
+ public async Task CreateTransferTxAsync(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, object data = null, bool addAssert = true)
{
var sender = Contract.CreateSignatureRedeemScript(fromKey.PublicKey).ToScriptHash();
Signer[] signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = sender } };
byte[] script = scriptHash.MakeScript("transfer", sender, to, amount, data);
+ if (addAssert) script = script.Concat(new[] { (byte)OpCode.ASSERT }).ToArray();
- TransactionManagerFactory factory = new TransactionManagerFactory(rpcClient);
+ TransactionManagerFactory factory = new(rpcClient);
TransactionManager manager = await factory.MakeTransactionAsync(script, signers).ConfigureAwait(false);
+
return await manager
.AddSignature(fromKey)
.SignAsync().ConfigureAwait(false);
@@ -156,17 +159,20 @@ public async Task CreateTransferTxAsync(UInt160 scriptHash, KeyPair
/// to account
/// transfer amount
/// onPayment data
+ /// Add assert at the end of the script
///
- public async Task CreateTransferTxAsync(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] fromKeys, UInt160 to, BigInteger amount, object data = null)
+ public async Task CreateTransferTxAsync(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] fromKeys, UInt160 to, BigInteger amount, object data = null, bool addAssert = true)
{
if (m > fromKeys.Length)
throw new ArgumentException($"Need at least {m} KeyPairs for signing!");
var sender = Contract.CreateMultiSigContract(m, pubKeys).ScriptHash;
Signer[] signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = sender } };
byte[] script = scriptHash.MakeScript("transfer", sender, to, amount, data);
+ if (addAssert) script = script.Concat(new[] { (byte)OpCode.ASSERT }).ToArray();
- TransactionManagerFactory factory = new TransactionManagerFactory(rpcClient);
+ TransactionManagerFactory factory = new(rpcClient);
TransactionManager manager = await factory.MakeTransactionAsync(script, signers).ConfigureAwait(false);
+
return await manager
.AddMultiSig(fromKeys, m, pubKeys)
.SignAsync().ConfigureAwait(false);
diff --git a/src/RpcClient/WalletAPI.cs b/src/RpcClient/WalletAPI.cs
index fc66fc131..8cac51763 100644
--- a/src/RpcClient/WalletAPI.cs
+++ b/src/RpcClient/WalletAPI.cs
@@ -109,11 +109,12 @@ public Task GetTokenBalanceAsync(string tokenHash, string account)
///
/// wif or private key
/// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005")
+ /// Add assert at the end of the script
/// The transaction sended
- public Task ClaimGasAsync(string key)
+ public Task ClaimGasAsync(string key, bool addAssert = true)
{
KeyPair keyPair = Utility.GetKeyPair(key);
- return ClaimGasAsync(keyPair);
+ return ClaimGasAsync(keyPair, addAssert);
}
///
@@ -121,12 +122,13 @@ public Task ClaimGasAsync(string key)
/// This function will transfer NEO balance from account to itself
///
/// keyPair
+ /// Add assert at the end of the script
/// The transaction sended
- public async Task ClaimGasAsync(KeyPair keyPair)
+ public async Task ClaimGasAsync(KeyPair keyPair, bool addAssert = true)
{
UInt160 toHash = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash();
BigInteger balance = await nep17API.BalanceOfAsync(NativeContract.NEO.Hash, toHash).ConfigureAwait(false);
- Transaction transaction = await nep17API.CreateTransferTxAsync(NativeContract.NEO.Hash, keyPair, toHash, balance).ConfigureAwait(false);
+ Transaction transaction = await nep17API.CreateTransferTxAsync(NativeContract.NEO.Hash, keyPair, toHash, balance, null, addAssert).ConfigureAwait(false);
await rpcClient.SendRawTransactionAsync(transaction).ConfigureAwait(false);
return transaction;
}
@@ -139,8 +141,10 @@ public async Task ClaimGasAsync(KeyPair keyPair)
/// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005")
/// address or account script hash
/// token amount
+ /// onPayment data
+ /// Add assert at the end of the script
///
- public async Task TransferAsync(string tokenHash, string fromKey, string toAddress, decimal amount, object data = null)
+ public async Task TransferAsync(string tokenHash, string fromKey, string toAddress, decimal amount, object data = null, bool addAssert = true)
{
UInt160 scriptHash = Utility.GetScriptHash(tokenHash, rpcClient.protocolSettings);
var decimals = await nep17API.DecimalsAsync(scriptHash).ConfigureAwait(false);
@@ -148,7 +152,7 @@ public async Task TransferAsync(string tokenHash, string fromKey, s
KeyPair from = Utility.GetKeyPair(fromKey);
UInt160 to = Utility.GetScriptHash(toAddress, rpcClient.protocolSettings);
BigInteger amountInteger = amount.ToBigInteger(decimals);
- return await TransferAsync(scriptHash, from, to, amountInteger, data).ConfigureAwait(false);
+ return await TransferAsync(scriptHash, from, to, amountInteger, data, addAssert).ConfigureAwait(false);
}
///
@@ -158,10 +162,12 @@ public async Task TransferAsync(string tokenHash, string fromKey, s
/// from KeyPair
/// to account script hash
/// transfer amount
+ /// onPayment data
+ /// Add assert at the end of the script
///
- public async Task TransferAsync(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger, object data = null)
+ public async Task TransferAsync(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger, object data = null, bool addAssert = true)
{
- Transaction transaction = await nep17API.CreateTransferTxAsync(scriptHash, from, to, amountInteger, data).ConfigureAwait(false);
+ Transaction transaction = await nep17API.CreateTransferTxAsync(scriptHash, from, to, amountInteger, data, addAssert).ConfigureAwait(false);
await rpcClient.SendRawTransactionAsync(transaction).ConfigureAwait(false);
return transaction;
}
@@ -175,10 +181,12 @@ public async Task TransferAsync(UInt160 scriptHash, KeyPair from, U
/// sign keys
/// to account
/// transfer amount
+ /// onPayment data
+ /// Add assert at the end of the script
///
- public async Task TransferAsync(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] keys, UInt160 to, BigInteger amountInteger, object data = null)
+ public async Task TransferAsync(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] keys, UInt160 to, BigInteger amountInteger, object data = null, bool addAssert = true)
{
- Transaction transaction = await nep17API.CreateTransferTxAsync(scriptHash, m, pubKeys, keys, to, amountInteger, data).ConfigureAwait(false);
+ Transaction transaction = await nep17API.CreateTransferTxAsync(scriptHash, m, pubKeys, keys, to, amountInteger, data, addAssert).ConfigureAwait(false);
await rpcClient.SendRawTransactionAsync(transaction).ConfigureAwait(false);
return transaction;
}
diff --git a/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs b/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs
index 566efc9e4..315f573e7 100644
--- a/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs
+++ b/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs
@@ -137,16 +137,20 @@ public async Task TestGetTokenInfo()
[TestMethod]
public async Task TestTransfer()
{
- byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000), null);
+ byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000), null)
+ .Concat(new[] { (byte)OpCode.ASSERT })
+ .ToArray();
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter());
var client = rpcClientMock.Object;
- var result = await nep17API.CreateTransferTxAsync(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000));
+ var result = await nep17API.CreateTransferTxAsync(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000), null, true);
- testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000), string.Empty);
+ testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000), string.Empty)
+ .Concat(new[] { (byte)OpCode.ASSERT })
+ .ToArray();
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter());
- result = await nep17API.CreateTransferTxAsync(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000), string.Empty);
+ result = await nep17API.CreateTransferTxAsync(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000), string.Empty, true);
Assert.IsNotNull(result);
}
}
diff --git a/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs b/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs
index 991c7f8b8..14ec43053 100644
--- a/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs
+++ b/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs
@@ -8,6 +8,7 @@
using Neo.SmartContract.Native;
using Neo.VM;
using Neo.Wallets;
+using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
@@ -89,7 +90,7 @@ public async Task TestClaimGas()
json["hash"] = UInt256.Zero.ToString();
rpcClientMock.Setup(p => p.RpcSendAsync("sendrawtransaction", It.IsAny())).ReturnsAsync(json);
- var tranaction = await walletAPI.ClaimGasAsync(keyPair1.Export());
+ var tranaction = await walletAPI.ClaimGasAsync(keyPair1.Export(), false);
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.Span.ToHexString());
}
@@ -99,14 +100,16 @@ public async Task TestTransfer()
byte[] decimalsScript = NativeContract.GAS.Hash.MakeScript("decimals");
UT_TransactionManager.MockInvokeScript(rpcClientMock, decimalsScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(8) });
- byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, NativeContract.GAS.Factor * 100, null);
+ byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, NativeContract.GAS.Factor * 100, null)
+ .Concat(new[] { (byte)OpCode.ASSERT })
+ .ToArray();
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) });
var json = new JObject();
json["hash"] = UInt256.Zero.ToString();
rpcClientMock.Setup(p => p.RpcSendAsync("sendrawtransaction", It.IsAny())).ReturnsAsync(json);
- var tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(client.protocolSettings.AddressVersion), 100);
+ var tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(client.protocolSettings.AddressVersion), 100, null, true);
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.Span.ToHexString());
}
@@ -121,19 +124,21 @@ public async Task TestTransferfromMultiSigAccount()
byte[] decimalsScript = NativeContract.GAS.Hash.MakeScript("decimals");
UT_TransactionManager.MockInvokeScript(rpcClientMock, decimalsScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(8) });
- byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", multiSender, UInt160.Zero, NativeContract.GAS.Factor * 100, null);
+ byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", multiSender, UInt160.Zero, NativeContract.GAS.Factor * 100, null)
+ .Concat(new[] { (byte)OpCode.ASSERT })
+ .ToArray();
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) });
var json = new JObject();
json["hash"] = UInt256.Zero.ToString();
rpcClientMock.Setup(p => p.RpcSendAsync("sendrawtransaction", It.IsAny())).ReturnsAsync(json);
- var tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 1, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100);
+ var tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 1, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100, null, true);
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.Span.ToHexString());
try
{
- tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 2, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100);
+ tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 2, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100, null, true);
Assert.Fail();
}
catch (System.Exception e)
@@ -141,10 +146,12 @@ public async Task TestTransferfromMultiSigAccount()
Assert.AreEqual(e.Message, $"Need at least 2 KeyPairs for signing!");
}
- testScript = NativeContract.GAS.Hash.MakeScript("transfer", multiSender, UInt160.Zero, NativeContract.GAS.Factor * 100, string.Empty);
+ testScript = NativeContract.GAS.Hash.MakeScript("transfer", multiSender, UInt160.Zero, NativeContract.GAS.Factor * 100, string.Empty)
+ .Concat(new[] { (byte)OpCode.ASSERT })
+ .ToArray();
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) });
- tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 1, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100, string.Empty);
+ tranaction = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 1, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100, string.Empty, true);
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.Span.ToHexString());
}
From e9d8683360ae968a79ad4e3a70e95fac27584938 Mon Sep 17 00:00:00 2001
From: Christopher Schuchardt
Date: Wed, 8 Nov 2023 02:23:39 -0500
Subject: [PATCH 12/30] Add Rest Web Server `WIP` (#810)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add new project for restserver
* Added Utils, Node and contract controllers
* updated license for each file.
* Added plugin support for plugins to add dependencies via dependency injection.
* Update to allow MaxPageSize
* Added new settings for configuration.
Added HTTP Basic Auth.
Changed most classes to internal that didnt need to be public.
Added Rest Server Middleware.
Added the ability to block urls paths (for module rest server plugins).
Added the ability to enable CORS and whitelist of urls.
Added HTTP user and pass configurable.
Added hot load of Controllers from plugin assemblies
Changed Controllers to be "ApiController" class
* Added CORS misconfiguation message to the user if auth and cors is enabled with no origins,.
* Added Wallet API (untested)
Added MaxTransactionFee to config
Added Anything for Kestrel to be configurable
* Added Nep17 tokens controller.
Added Nep11 token controller.
Changed namespaces and file names around.
* Added invoke gas max amount
Added peers route for node controller
Changed RemoteNodeModel and TokenBalanceModel namespaces
* Added blockchain controller
Added Neo/Gas controller
Added send raw transactions relay
* Changed controllers to show error if is invalid node protocol network
Fixed formating
Added blacklist/disable controllers
Removed DisableRoute list from config (wasn't being used)
* Fixed RestServerMiddleware crashing if malformed basic auth is sent from end user
* Changed json converter for UInt160 to validate Address and scripthash format
Added InvokeScript to helper class
Added ConvertToScriptHash for address to scripthash
* Changes Namespaces
* Fixed Wallet Transfers
* Error Codes
* Added Custom Exceptions
Changed all controller parameters for json converters
Added custom error handling
Fixed wallet sessions
Added wallet session manager
removed development mode (not needed)
Added session timeout to config file
Added max allow sessions to config file
* Added swagger support
Added swagger configurable in config.json
Changed controllers to reflect swagger
Changed wallet controller to reset expiring for wallets per request
* Made RestServer more generic and plugin friendly
Added new json converter for block, transaction and contract
removed the model associated with blocks, transactions, and contracts
changed json settings for new json converters that were added
* Changed now when wallets expire they will be terminated on time.
Added Console Commands for wallet sessions
* Fixed Block, transaction json converters for null values.
Fixed UInt160, UInt256 json converters value not being set right in certain situations.
Fixed wallet transfers for null signers
* Fixed UInt160 and UInt256 with null values
Fixed Wallet transfers with invalid data or output messages
* Added full wallet functionality
Changed wallet route paths around to be suit the means
Fixed ECPoint json converter for reading
Fixed UInt160 json converter
Changed error out for invalid serialization of json on route parameters
Fixed with dotnet format
* Added Transactions with scripts
Changed restricted wallet path to Environment.CurrentDirectory
* Fixed typos with json properties in json converters.
Changed NEP-11 Models to IReadOnlyDictionary and IEnumerable.
* Changed Nep11 and Nep17 token classes to be more sounds on logic
Fixed with dotnet format
* Added swagger server discriptions for wallet controller.
Fixed Guid json converter with values.
* Fixes the transaction model for swagger
* Added Swagger service descriptions
* Fixed typos in swagger descriptions
* Changed xml commecnts to RestServer only Because you can only use one xml file per swagger.
Fixed some more typos
* Removed GenerateDocumentationFile kind of point if other plugins cant add on to it.
* Changed ExecutionEngineModel to be more swagger friendly
* Fixed all the swagger json objects descriptions, types are now readable from the Swagger UI
Added back xml comments does work with other plugins in swagger.
Added SchemaFilter for removing unwanted properties from json objects in swagger.
Fixed some type mapping for swagger
Removed some type mapping for swagger
* Removed unnecessary namespaces from file(s)
* Changed WalletController to have expose more information for some errors
Changed Swagger doc description and title
* Changed to Swagger to support only OpenAPI version 2.0 for DotNet Standard 2.0/2.1
* Fixed CORS wasnt being set.
* Changed WalletTimeout to WalletSessionTimeout
Added WWWAuthenticate support for browsers
Fixed wording of the 400 responses
* Code clean up
Changed MaxInvokeGas to MaxGasInvoke to match Rpc Server's
* Changed names of controller from token to tokens
Add basic auth handler
removed not needed json converters
added uint160 model binder
merge
* Added basic auth handler
Added jsonconverters
Moved Controllers to V1 folder for version control
* Update timer to not be runaway timer
* Updated Json Converter Settings
* Added RestServer Docs
---------
Co-authored-by: Christopher R. Schuchardt
Co-authored-by: Vitor Nazário Coelho
---
docs/RestServer/Addons.md | 108 +++
docs/RestServer/ConfigFile.md | 56 ++
docs/RestServer/README.md | 136 ++++
neo-modules.sln | 7 +
.../BasicAuthenticationHandler.cs | 62 ++
src/RestServer/Binder/UInt160Binder.cs | 47 ++
.../Binder/UInt160BinderProvider.cs | 33 +
.../Controllers/v1/ContractsController.cs | 214 ++++++
.../Controllers/v1/LedgerController.cs | 389 +++++++++++
.../Controllers/v1/NodeController.cs | 86 +++
.../Controllers/v1/TokensController.cs | 304 +++++++++
.../Controllers/v1/UtilsController.cs | 106 +++
.../Controllers/v1/WalletController.cs | 646 ++++++++++++++++++
.../Exceptions/AddressFormatException.cs | 18 +
.../Exceptions/ApplicationEngineException.cs | 18 +
.../Exceptions/BlockNotFoundException.cs | 18 +
.../Exceptions/ContractNotFoundException.cs | 18 +
.../InvalidParameterRangeException.cs | 18 +
.../JsonPropertyNullOrEmptyException.cs | 19 +
.../Exceptions/Nep11NotSupportedException.cs | 18 +
.../Exceptions/Nep17NotSupportedException.cs | 18 +
src/RestServer/Exceptions/NodeException.cs | 18 +
.../Exceptions/NodeNetworkException.cs | 18 +
.../QueryParameterNotFoundException.cs | 18 +
src/RestServer/Exceptions/RestErrorCodes.cs | 19 +
.../Exceptions/ScriptHashFormatException.cs | 18 +
.../TransactionNotFoundException.cs | 18 +
.../Exceptions/UInt256FormatException.cs | 18 +
src/RestServer/Exceptions/WalletException.cs | 18 +
.../WalletInsufficientFundsException.cs | 18 +
.../Exceptions/WalletOpenException.cs | 19 +
.../Exceptions/WalletSessionException.cs | 18 +
.../Extensions/LedgerContractExtensions.cs | 142 ++++
src/RestServer/Extensions/ModelExtensions.cs | 99 +++
.../Extensions/UInt160Extensions.cs | 27 +
src/RestServer/Helpers/ContractHelper.cs | 58 ++
src/RestServer/Helpers/ScriptHelper.cs | 59 ++
.../Middleware/RestServerMiddleware.cs | 43 ++
.../Models/Blockchain/AccountDetails.cs | 33 +
src/RestServer/Models/CountModel.cs | 21 +
src/RestServer/Models/Error/ErrorModel.cs | 32 +
.../Error/ParameterFormatExceptionModel.cs | 28 +
src/RestServer/Models/ExecutionEngineModel.cs | 49 ++
.../Models/Ledger/MemoryPoolCountModel.cs | 31 +
src/RestServer/Models/Node/PluginModel.cs | 31 +
.../Models/Node/ProtocolSettingsModel.cs | 40 ++
src/RestServer/Models/Node/RemoteNodeModel.cs | 36 +
.../Models/Token/NEP11TokenModel.cs | 19 +
.../Models/Token/NEP17TokenModel.cs | 23 +
.../Models/Token/TokenBalanceModel.cs | 24 +
.../Models/Utils/UtilsAddressIsValidModel.cs | 11 +
.../Models/Utils/UtilsAddressModel.cs | 21 +
.../Models/Utils/UtilsScriptHashModel.cs | 21 +
.../Wallet/WalletAccountBalanceModel.cs | 26 +
.../Models/Wallet/WalletAddressModel.cs | 51 ++
.../Models/Wallet/WalletAssetModel.cs | 54 ++
.../Wallet/WalletChangePasswordModel.cs | 40 ++
.../Models/Wallet/WalletCreateAccountModel.cs | 24 +
.../Models/Wallet/WalletCreateModel.cs | 45 ++
.../Models/Wallet/WalletExportKeyModel.cs | 34 +
.../Models/Wallet/WalletImportKey.cs | 27 +
.../WalletImportMultiSigAddressModel.cs | 32 +
.../Models/Wallet/WalletKeyModel.cs | 33 +
.../Wallet/WalletMultiSignContractModel.cs | 34 +
.../Models/Wallet/WalletOpenModel.cs | 33 +
.../Models/Wallet/WalletSendModel.cs | 58 ++
.../Models/Wallet/WalletSessionModel.cs | 24 +
.../Wallet/WalletTransactionScriptModel.cs | 35 +
.../Json/BigDecimalJsonConverter.cs | 48 ++
.../Json/BlockHeaderJsonConverter.cs | 32 +
.../Newtonsoft/Json/BlockJsonConverter.cs | 30 +
.../Json/ContractAbiJsonConverter.cs | 28 +
.../ContractEventDescriptorJsonConverter.cs | 28 +
.../Json/ContractGroupJsonConverter.cs | 28 +
.../Newtonsoft/Json/ContractJsonConverter.cs | 28 +
.../Json/ContractManifestJsonConverter.cs | 28 +
.../Json/ContractMethodJsonConverter.cs | 28 +
.../ContractMethodParametersJsonConverter.cs | 27 +
...ontractParameterDefinitionJsonConverter.cs | 28 +
.../Json/ContractParameterJsonConverter.cs | 33 +
...ntractPermissionDescriptorJsonConverter.cs | 28 +
.../Json/ContractPermissionJsonConverter.cs | 28 +
.../Newtonsoft/Json/ECPointJsonConverter.cs | 37 +
.../Newtonsoft/Json/GuidJsonConverter.cs | 27 +
.../Json/InteropInterfaceJsonConverter.cs | 31 +
.../Json/MethodTokenJsonConverter.cs | 28 +
.../Newtonsoft/Json/NefFileJsonConverter.cs | 24 +
.../Json/ReadOnlyMemoryBytesJsonConverter.cs | 29 +
.../Newtonsoft/Json/SignerJsonConverter.cs | 29 +
.../Newtonsoft/Json/StackItemJsonConverter.cs | 33 +
.../Json/TransactionAttributeJsonConverter.cs | 28 +
.../Json/TransactionJsonConverter.cs | 28 +
.../Newtonsoft/Json/UInt160JsonConverter.cs | 39 ++
.../Newtonsoft/Json/UInt256JsonConverter.cs | 36 +
.../Newtonsoft/Json/VmArrayJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmBooleanJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmBufferJsonConverter.cs | 31 +
.../Json/VmByteStringJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmIntegerJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmMapJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmNullJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmPointerJsonConverter.cs | 31 +
.../Newtonsoft/Json/VmStructJsonConverter.cs | 31 +
.../Json/WitnessConditionJsonConverter.cs | 95 +++
.../Newtonsoft/Json/WitnessJsonConverter.cs | 32 +
.../Json/WitnessRuleJsonConverter.cs | 24 +
.../BlackListControllerFeatureProvider.cs | 35 +
src/RestServer/RestServer.csproj | 25 +
src/RestServer/RestServerPlugin.Console.cs | 131 ++++
src/RestServer/RestServerPlugin.cs | 62 ++
src/RestServer/RestServerSettings.cs | 162 +++++
src/RestServer/RestServerUtility.JTokens.cs | 302 ++++++++
src/RestServer/RestServerUtility.cs | 346 ++++++++++
src/RestServer/RestWebServer.cs | 417 +++++++++++
src/RestServer/Tokens/NEP11Token.cs | 178 +++++
src/RestServer/Tokens/NEP17Token.cs | 75 ++
src/RestServer/WalletSessionManager.cs | 54 ++
src/RestServer/config.json | 27 +
118 files changed, 6972 insertions(+)
create mode 100644 docs/RestServer/Addons.md
create mode 100644 docs/RestServer/ConfigFile.md
create mode 100644 docs/RestServer/README.md
create mode 100644 src/RestServer/Authentication/BasicAuthenticationHandler.cs
create mode 100644 src/RestServer/Binder/UInt160Binder.cs
create mode 100644 src/RestServer/Binder/UInt160BinderProvider.cs
create mode 100644 src/RestServer/Controllers/v1/ContractsController.cs
create mode 100644 src/RestServer/Controllers/v1/LedgerController.cs
create mode 100644 src/RestServer/Controllers/v1/NodeController.cs
create mode 100644 src/RestServer/Controllers/v1/TokensController.cs
create mode 100644 src/RestServer/Controllers/v1/UtilsController.cs
create mode 100644 src/RestServer/Controllers/v1/WalletController.cs
create mode 100644 src/RestServer/Exceptions/AddressFormatException.cs
create mode 100644 src/RestServer/Exceptions/ApplicationEngineException.cs
create mode 100644 src/RestServer/Exceptions/BlockNotFoundException.cs
create mode 100644 src/RestServer/Exceptions/ContractNotFoundException.cs
create mode 100644 src/RestServer/Exceptions/InvalidParameterRangeException.cs
create mode 100644 src/RestServer/Exceptions/JsonPropertyNullOrEmptyException.cs
create mode 100644 src/RestServer/Exceptions/Nep11NotSupportedException.cs
create mode 100644 src/RestServer/Exceptions/Nep17NotSupportedException.cs
create mode 100644 src/RestServer/Exceptions/NodeException.cs
create mode 100644 src/RestServer/Exceptions/NodeNetworkException.cs
create mode 100644 src/RestServer/Exceptions/QueryParameterNotFoundException.cs
create mode 100644 src/RestServer/Exceptions/RestErrorCodes.cs
create mode 100644 src/RestServer/Exceptions/ScriptHashFormatException.cs
create mode 100644 src/RestServer/Exceptions/TransactionNotFoundException.cs
create mode 100644 src/RestServer/Exceptions/UInt256FormatException.cs
create mode 100644 src/RestServer/Exceptions/WalletException.cs
create mode 100644 src/RestServer/Exceptions/WalletInsufficientFundsException.cs
create mode 100644 src/RestServer/Exceptions/WalletOpenException.cs
create mode 100644 src/RestServer/Exceptions/WalletSessionException.cs
create mode 100644 src/RestServer/Extensions/LedgerContractExtensions.cs
create mode 100644 src/RestServer/Extensions/ModelExtensions.cs
create mode 100644 src/RestServer/Extensions/UInt160Extensions.cs
create mode 100644 src/RestServer/Helpers/ContractHelper.cs
create mode 100644 src/RestServer/Helpers/ScriptHelper.cs
create mode 100644 src/RestServer/Middleware/RestServerMiddleware.cs
create mode 100644 src/RestServer/Models/Blockchain/AccountDetails.cs
create mode 100644 src/RestServer/Models/CountModel.cs
create mode 100644 src/RestServer/Models/Error/ErrorModel.cs
create mode 100644 src/RestServer/Models/Error/ParameterFormatExceptionModel.cs
create mode 100644 src/RestServer/Models/ExecutionEngineModel.cs
create mode 100644 src/RestServer/Models/Ledger/MemoryPoolCountModel.cs
create mode 100644 src/RestServer/Models/Node/PluginModel.cs
create mode 100644 src/RestServer/Models/Node/ProtocolSettingsModel.cs
create mode 100644 src/RestServer/Models/Node/RemoteNodeModel.cs
create mode 100644 src/RestServer/Models/Token/NEP11TokenModel.cs
create mode 100644 src/RestServer/Models/Token/NEP17TokenModel.cs
create mode 100644 src/RestServer/Models/Token/TokenBalanceModel.cs
create mode 100644 src/RestServer/Models/Utils/UtilsAddressIsValidModel.cs
create mode 100644 src/RestServer/Models/Utils/UtilsAddressModel.cs
create mode 100644 src/RestServer/Models/Utils/UtilsScriptHashModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletAccountBalanceModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletAddressModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletAssetModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletChangePasswordModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletCreateAccountModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletCreateModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletExportKeyModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletImportKey.cs
create mode 100644 src/RestServer/Models/Wallet/WalletImportMultiSigAddressModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletKeyModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletMultiSignContractModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletOpenModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletSendModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletSessionModel.cs
create mode 100644 src/RestServer/Models/Wallet/WalletTransactionScriptModel.cs
create mode 100644 src/RestServer/Newtonsoft/Json/BigDecimalJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/BlockHeaderJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/BlockJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractAbiJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractEventDescriptorJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractGroupJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractManifestJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractMethodJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractMethodParametersJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractParameterDefinitionJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractParameterJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractPermissionDescriptorJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ContractPermissionJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ECPointJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/GuidJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/InteropInterfaceJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/MethodTokenJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/NefFileJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/ReadOnlyMemoryBytesJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/SignerJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/StackItemJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/TransactionAttributeJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/TransactionJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/UInt160JsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/UInt256JsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmArrayJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmBooleanJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmBufferJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmByteStringJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmIntegerJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmMapJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmNullJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmPointerJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/VmStructJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/WitnessConditionJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/WitnessJsonConverter.cs
create mode 100644 src/RestServer/Newtonsoft/Json/WitnessRuleJsonConverter.cs
create mode 100644 src/RestServer/Providers/BlackListControllerFeatureProvider.cs
create mode 100644 src/RestServer/RestServer.csproj
create mode 100644 src/RestServer/RestServerPlugin.Console.cs
create mode 100644 src/RestServer/RestServerPlugin.cs
create mode 100644 src/RestServer/RestServerSettings.cs
create mode 100644 src/RestServer/RestServerUtility.JTokens.cs
create mode 100644 src/RestServer/RestServerUtility.cs
create mode 100644 src/RestServer/RestWebServer.cs
create mode 100644 src/RestServer/Tokens/NEP11Token.cs
create mode 100644 src/RestServer/Tokens/NEP17Token.cs
create mode 100644 src/RestServer/WalletSessionManager.cs
create mode 100644 src/RestServer/config.json
diff --git a/docs/RestServer/Addons.md b/docs/RestServer/Addons.md
new file mode 100644
index 000000000..07be39f4b
--- /dev/null
+++ b/docs/RestServer/Addons.md
@@ -0,0 +1,108 @@
+## RestServer Plugin
+In this secion of you will learn how to make a `neo-cli` plugin that integrates with `RestServer`
+plugin. Lets take a look at [Example Plugin](/examples/RestServerPlugin).
+
+- No reference to `RestServer` is reqired.
+- Requires DotNet 7.0
+
+## Folder Structure
+```bash
+Project
+├── Controllers
+│ └── ExampleController.cs
+├── ExamplePlugin.cs
+├── ExamplePlugin.csproj
+├── Exceptions
+│ └── CustomException.cs
+└── Models
+ └── ErrorModel.cs
+```
+The only thing that is important here is the `controllers` folder. This folder is required for the `RestServer`
+plugin to register the controllers in its web server. This location is where you put all your controllers.
+
+## Controllers
+The `controller` class is the same as ASP.Net Core's. Controllers must have their attribute set
+as `[ApiController]` and inherent from `ControllerBase`.
+
+## Swagger Controller
+A `Swagger` controller uses special attributes that are set on your controller's class.
+
+**Controller Class Attributes**
+- `[Produces(MediaTypeNames.Application.Json)]` (_Required_)
+- `[Consumes(MediaTypeNames.Application.Json)]` (_Required_)
+- `[ApiExplorerSettings(GroupName = "v1")]`
+ - **GroupName** - _is which version of the API you are targeting._
+- `[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]` (_Required_)
+ - **Type** - _Must have a base class of [error](#error-class)._
+
+## Error Class
+Needs to be the same as `RestServer` of else there will be some inconsistencies
+with end users not knowing which type to use. This class can be `public` or `internal`.
+Properties `Code`, `Name` and `Message` values can be whatever you desire.
+
+**Model**
+```csharp
+public class ErrorModel
+{
+ public int Code { get; set; };
+ public string Name { get; set; };
+ public string Message { get; set; };
+}
+```
+
+## Controller Actions
+Controller actions need to have special attributes as well as code comments.
+
+- `[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(string))]`
+
+HTTP status code `200 (OK)` is required with return type defined. You can use more than one attribute. One per HTTP status code.
+
+### Action Example
+```csharp
+[HttpGet("contracts/{hash:required}/sayHello", Name = "GetSayHello")]
+[ProducesResponseType(StatusCodes.Status204NoContent)]
+[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(string))]
+public IActionResult GetSayHello(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+{
+ if (scripthash == UInt160.Zero)
+ return NoContent();
+ return Ok($"Hello, {scripthash}");
+}
+```
+Notice that the _above_ example also returns with HTTP status code of `204 No Content`.
+This action `route` also extends the `contracts` API. Adding method `sayHello`. Routes
+can be what you like as well. But if you want to extend on any existing controller you
+must use existing routes paths.
+
+### Path(s)
+- `/api/v1/contracts/`
+- `/api/v1/ledger/`
+- `/api/v1/node/`
+- `/api/v1/tokens`
+- `/api/v1/Utils/`
+
+### Excluded Path(s)
+- `/api/v1/wallet/`
+
+_for security reasons_.
+
+### Code Comments for Swagger
+```csharp
+///
+///
+///
+///
+///
+/// Successful
+/// An error occurred. See Response for details.
+```
+
+Also note that you need to have `GenerateDocumentationFile` enabled in your
+`.csproj` file. The `xml` file that is generated; in our case would be `RestServerPlugin.xml`.
+This file gets put in same directory `Plugins/RestServerPlugin/` which is in the root of `neo-node`
+executable folder. Where you will see `neo-cli.exe`.
+
+File `RestServerPlugin.xml` will get added to `Swagger` automatically by the `RestServer`
+plugin.
diff --git a/docs/RestServer/ConfigFile.md b/docs/RestServer/ConfigFile.md
new file mode 100644
index 000000000..303a920f1
--- /dev/null
+++ b/docs/RestServer/ConfigFile.md
@@ -0,0 +1,56 @@
+## Table
+
+| Name | Type | Description |
+| :--- | :---: | :--- |
+|**Network**|_uint32_|_Network you would like the `RestServer` to be enabled on._|
+|**BindAddress**|_string_|_Ip address of the interface you want to bind too._|
+|**Port**|_uint32_|_Port number to bind too._|
+|**KeepAliveTimeout**|_uint32_|_Time to keep the request alive, in seconds._|
+|**SslCertFile**|_string_|_Is the path and file name of a certificate file, relative to the directory that contains the node's executable files._|
+|**SslCertPassword**|_string_|_Is the password required to access the `X.509` certificate data._|
+|**TrustedAuthorities**|_StringArray_|_Tumbprints of the of the last certificate authority in the chain._|
+|**EnableBasicAuthentication**|_boolean_|_enables basic authentication._|
+|**RestUser**|_string_|_Basic authentication's `username`._|
+|**RestPass**|_string_|_Basic authentication's `password`._|
+|**EnableCors**|_boolean_|_Enables Cross-origin resource sharing (`CORS`). Note by default it enables `*` any origin._|
+|**AllowOrigins**|_StringArray_|_A list of the origins to allow. Note needs to add origins for basic auth to work with `CORS`._|
+|**DisableControllers**|_StringArray_|_A list of `controllers` to be disabled. Requires restart of the node, if changed._|
+|**EnableCompression**|_boolean_|_Enables `GZip` data compression._|
+|**CompressionLevel**|_enum_|_Compression level. Values can be `Fastest`, `Optimal`, `NoCompression` or `SmallestSize`_|
+|**EnableForwardedHeaders**|_boolean_|_Enables response/request headers for proxy forwarding. (data center usage)_|
+|**EnableSwagger**|_boolean_|_Enables `Swagger` with `Swagger UI` for the rest services._|
+|**MaxPageSize**|_uint32_|_Max page size for searches on `Ledger`/`Contracts` route._|
+|**MaxConcurrentConnections**|_int64_|_Max allow concurrent HTTP connections._|
+|**MaxTransactionFee**|_int64_|_Max transaction fee for `wallet` transfers._|
+|**MaxInvokeGas**|_int64_|_Max gas to be invoked on the `Neo` virtual machine._|
+|**WalletSessionTimeout**|_uint32_|_When `wallet` session expires, in seconds._|
+
+## Default "Config.json" file
+```json
+{
+ "PluginConfiguration": {
+ "Network": 860833102,
+ "BindAddress": "127.0.0.1",
+ "Port": 10339,
+ "KeepAliveTimeout": 120,
+ "SslCertFile": "",
+ "SslCertPassword": "",
+ "TrustedAuthorities": [],
+ "EnableBasicAuthentication": false,
+ "RestUser": "",
+ "RestPass": "",
+ "EnableCors": true,
+ "AllowOrigins": [],
+ "DisableControllers": [ "WalletController" ],
+ "EnableCompression": true,
+ "CompressionLevel": "SmallestSize",
+ "EnableForwardedHeaders": false,
+ "EnableSwagger": true,
+ "MaxPageSize": 50,
+ "MaxConcurrentConnections": 40,
+ "MaxTransactionFee": 10000000,
+ "MaxInvokeGas": 20000000,
+ "WalletSessionTimeout": 120
+ }
+}
+```
diff --git a/docs/RestServer/README.md b/docs/RestServer/README.md
new file mode 100644
index 000000000..05b068776
--- /dev/null
+++ b/docs/RestServer/README.md
@@ -0,0 +1,136 @@
+## RestServer
+In this section you will learn about `RestServer` plugin and how it works.
+
+See [config.json](ConfigFile.md) for information about the configurations.
+
+## Dependencies
+- **Microsoft.AspNetCore.JsonPatch.dll** `Required`
+- **Microsoft.AspNetCore.Mvc.NewtonsoftJson.dll** `Required`
+- **Microsoft.OpenApi.dll** `Swagger`
+- **Swashbuckle.AspNetCore.Swagger.dll** `Swagger`
+- **Swashbuckle.AspNetCore.SwaggerGen.dll** `Swagger`
+- **Swashbuckle.AspNetCore.SwaggerUI.dll** `Swagger`
+- **Swashbuckle.AspNetCore.Newtonsoft.dll** `Swagger`
+- **RestServer.xml** `Swagger UI`
+
+These files go in the same directory as the `RestServer.dll`. In neo-cli
+`plugins/RestServer/` folder.
+
+## Response Headers
+| Name | Value(s) | Description |
+| :---: | --- | :--- |
+|**server**|_neo-cli/3.6.0 RestServer/3.6.0_|_`neo-cli` and `RestServer` version._|
+
+Custom headers can be added by [Neo RestServer Plugins](Addons.md).
+
+## JSON Serializer
+`RestServer` uses custom Newtonsoft Json Converters to serialize controller action
+responses and `route` parameters.
+
+**One Way Binding** - `Write` only.
+- `Neo.SmartContract.ContractState`
+- `Neo.SmartContract.NefFile`
+- `Neo.SmartContract.MethodToken`
+- `Neo.SmartContract.Native.TrimmedBlock`
+- `Neo.SmartContract.Manifest.ContractAbi`
+- `Neo.SmartContract.Manifest.ContractGroup`
+- `Neo.SmartContract.Manifest.ContractManifest`
+- `Neo.SmartContract.Manifest.ContractPermission`
+- `Neo.SmartContract.Manifest.ContractPermissionDescriptor`
+- `Neo.Network.P2P.Payloads.Block`
+- `Neo.Network.P2P.Payloads.Header`
+- `Neo.Network.P2P.Payloads.Signer`
+- `Neo.Network.P2P.Payloads.TransactionAttribute`
+- `Neo.Network.P2P.Payloads.Transaction`
+- `Neo.Network.P2P.Payloads.Witness`
+
+**Two Way Binding** - `Read` & `Write`
+- `System.Guid`
+- `System.ReadOnlyMemory`
+- `Neo.BigDecimal`
+- `Neo.UInt160`
+- `Neo.UInt256`
+- `Neo.Cryptography.ECC.ECPoint`
+- `Neo.VM.Types.Array`
+- `Neo.VM.Types.Boolean`
+- `Neo.VM.Types.Buffer`
+- `Neo.VM.Types.ByteString`
+- `Neo.VM.Types.Integer`
+- `Neo.VM.Types.InteropInterface`
+- `Neo.VM.Types.Null`
+- `Neo.VM.Types.Map`
+- `Neo.VM.Types.Pointer`
+- `Neo.VM.Types.StackItem`
+- `Neo.VM.Types.Struct`
+
+## Remote Endpoints
+Parametes `{hash}` can be any Neo N3 address or scripthash; `{address}` can be any Neo N3 address **only**; `{number}` and `{index}` can be any _**uint32**_.
+
+**Parameter Examples**
+- `{hash}` - _0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5_ **or** _NiHURyS83nX2mpxtA7xq84cGxVbHojj5Wc_
+- `{address}` - _NiHURyS83nX2mpxtA7xq84cGxVbHojj5Wc_
+- `{number}` - _1_
+- `{index}` - _2500000_
+
+**Paths**
+- Utils
+ - `[GET]` `/api/v1/utils/{hash}/address`
+ - `[GET]` `/api/v1/utils/{address}/scripthash`
+ - `[GET]` `/api/v1/utils/{hash}/{address}/validate`
+- Node
+ - `[GET]` `/api/v1/node`
+ - `[GET]` `/api/v1/node/peers`
+ - `[GET]` `/api/v1/node/plugins`
+ - `[GET]` `/api/v1/node/settings`
+- Ledger
+ - `[GET]` `/api/v1/ledger/neo/accounts`
+ - `[GET]` `/api/v1/ledger/gas/accounts`
+ - `[GET]` `/api/v1/ledger/blocks?page={number}&size={number}`
+ - `[GET]` `/api/v1/ledger/blocks/height`
+ - `[GET]` `/api/v1/ledger/blocks/{index}`
+ - `[GET]` `/api/v1/ledger/blocks/{index}/header`
+ - `[GET]` `/api/v1/ledger/blocks/{index}/witness`
+ - `[GET]` `/api/v1/ledger/blocks/{index}/transactions?page={number}&size={number}`
+ - `[GET]` `/api/v1/ledger/transactions/{hash}`
+ - `[GET]` `/api/v1/ledger/transactions/{hash}/witnesses`
+ - `[GET]` `/api/v1/ledger/transactions/{hash}/signers`
+ - `[GET]` `/api/v1/ledger/transactions/{hash}/atributes`
+ - `[GET]` `/api/v1/ledger/memorypool?page={number}&size={number}`
+ - `[GET]` `/api/v1/ledger/memorypool/verified?page={number}&size={number}`
+ - `[GET]` `/api/v1/ledger/memorypool/unverified?page={number}&size={number}`
+ - `[GET]` `/api/v1/ledger/memorypool/count`
+- Tokens
+ - `[GET]` `/api/v1/tokens/balanceof/{address}`
+ - NFTs
+ - `[GET]` `/api/v1/tokens/nep-11?page={number}&size={number}`
+ - `[GET]` `/api/v1/tokens/nep-11/count`
+ - `[GET]` `/api/v1/tokens/nep-11/{hash}/balanceof/{address}`
+ - NEP-17
+ - `[GET]` `/api/v1/tokens/nep-17?page={number}&size={number}`
+ - `[GET]` `/api/v1/tokens/nep-17/count`
+ - `[GET]` `/api/v1/tokens/nep-17/{hash}/balanceof/{address}`
+- Contracts
+ - `[GET]` `/api/v1/contracts?page={number}&size={number}`
+ - `[GET]` `/api/v1/contracts/count`
+ - `[GET]` `/api/v1/contracts/{hash}`
+ - `[GET]` `/api/v1/contracts/{hash}/abi`
+ - `[GET]` `/api/v1/contracts/{hash}/manifest`
+ - `[GET]` `/api/v1/contracts/{hash}/nef`
+ - `[GET]` `/api/v1/contracts/{hash}/storage`
+- Wallet
+ - `[POST]` `/api/v1/wallet/open`
+ - `[POST]` `/api/v1/wallet/create`
+ - `[POST]` `/api/v1/wallet/{session}/address/create`
+ - `[GET]` `/api/v1/wallet/{session}/address/list`
+ - `[GET]` `/api/v1/wallet/{session}/asset/list`
+ - `[GET]` `/api/v1/wallet/{session}/balance/list`
+ - `[POST]` `/api/v1/wallet/{session}/changepassword`
+ - `[GET]` `/api/v1/wallet/{session}/close`
+ - `[GET]` `/api/v1/wallet/{session}/delete/{address}`
+ - `[GET]` `/api/v1/wallet/{session}/export/{address}`
+ - `[GET]` `/api/v1/wallet/{session}/export`
+ - `[GET]` `/api/v1/wallet/{session}/gas/unclaimed`
+ - `[GET]` `/api/v1/wallet/{session}/key/list`
+ - `[POST]` `/api/v1/wallet/{session}/import`
+ - `[POST]` `/api/v1/wallet/{session}/import/multisigaddress`
+ - `[POST]` `/api/v1/wallet/{session}/transfer`
diff --git a/neo-modules.sln b/neo-modules.sln
index 8a1931586..926fc514a 100644
--- a/neo-modules.sln
+++ b/neo-modules.sln
@@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLiteWallet", "src\SQLiteW
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StorageDumper", "src\StorageDumper\StorageDumper.csproj", "{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestServer", "src\RestServer\RestServer.csproj", "{A893825D-65FF-4FA9-85EF-16F3AC626A29}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -121,6 +123,10 @@ Global
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A893825D-65FF-4FA9-85EF-16F3AC626A29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A893825D-65FF-4FA9-85EF-16F3AC626A29}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A893825D-65FF-4FA9-85EF-16F3AC626A29}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A893825D-65FF-4FA9-85EF-16F3AC626A29}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -144,6 +150,7 @@ Global
{8D2EE375-2E2D-45FE-A4E9-0254D12C7554} = {59D802AB-C552-422A-B9C3-64D329FBCDCC}
{D121D57A-512E-4F74-ADA1-24482BF5C42B} = {97E81C78-1637-481F-9485-DA1225E94C23}
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC} = {97E81C78-1637-481F-9485-DA1225E94C23}
+ {A893825D-65FF-4FA9-85EF-16F3AC626A29} = {97E81C78-1637-481F-9485-DA1225E94C23}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61D3ADE6-BBFC-402D-AB42-1C71C9F9EDE3}
diff --git a/src/RestServer/Authentication/BasicAuthenticationHandler.cs b/src/RestServer/Authentication/BasicAuthenticationHandler.cs
new file mode 100644
index 000000000..17eb614fb
--- /dev/null
+++ b/src/RestServer/Authentication/BasicAuthenticationHandler.cs
@@ -0,0 +1,62 @@
+// Copyright (C) 2015-2023 neo-restful-plugin.
+//
+// The RestServer is free software distributed under the MIT software
+// license, see the accompanying file LICENSE in the main directory of
+// the project 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.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Neo.Plugins.RestServer;
+using System.Net.Http.Headers;
+using System.Security.Claims;
+using System.Text;
+using System.Text.Encodings.Web;
+
+namespace RestServer.Authentication
+{
+ internal class BasicAuthenticationHandler : AuthenticationHandler
+ {
+ public BasicAuthenticationHandler(
+ IOptionsMonitor options,
+ ILoggerFactory logger,
+ UrlEncoder encoder,
+ ISystemClock clock) : base(options, logger, encoder, clock)
+ {
+ }
+
+ protected override Task HandleAuthenticateAsync()
+ {
+ var authHeader = Request.Headers.Authorization;
+ if (string.IsNullOrEmpty(authHeader) == false && AuthenticationHeaderValue.TryParse(authHeader, out var authValue))
+ {
+ if (authValue.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) && authValue.Parameter != null)
+ {
+ try
+ {
+ var decodedParams = Encoding.UTF8.GetString(Convert.FromBase64String(authValue.Parameter));
+ var creds = decodedParams.Split(':', 2);
+ if (creds[0] == RestServerSettings.Current.RestUser && creds[1] == RestServerSettings.Current.RestPass)
+ {
+ var claims = new[] { new Claim(ClaimTypes.NameIdentifier, creds[0]) };
+ var identity = new ClaimsIdentity(claims, Scheme.Name);
+ var principal = new ClaimsPrincipal(identity);
+ var ticket = new AuthenticationTicket(principal, Scheme.Name);
+
+ return Task.FromResult(AuthenticateResult.Success(ticket));
+ }
+
+ }
+ catch (FormatException)
+ {
+ }
+ }
+ }
+ return Task.FromResult(AuthenticateResult.Fail("Authentication Failed!!!"));
+ }
+ }
+}
diff --git a/src/RestServer/Binder/UInt160Binder.cs b/src/RestServer/Binder/UInt160Binder.cs
new file mode 100644
index 000000000..fa72ad595
--- /dev/null
+++ b/src/RestServer/Binder/UInt160Binder.cs
@@ -0,0 +1,47 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Mvc.ModelBinding;
+
+namespace Neo.Plugins.RestServer.Binder
+{
+ internal class UInt160Binder : IModelBinder
+ {
+
+ public Task BindModelAsync(ModelBindingContext bindingContext)
+ {
+ _ = bindingContext ?? throw new ArgumentNullException(nameof(bindingContext));
+
+ if (bindingContext.BindingSource == BindingSource.Path ||
+ bindingContext.BindingSource == BindingSource.Query)
+ {
+ var modelName = bindingContext.ModelName;
+
+ // Try to fetch the value of the argument by name
+ var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
+
+ if (valueProviderResult == ValueProviderResult.None)
+ return Task.CompletedTask;
+
+ bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
+
+ var value = valueProviderResult.FirstValue;
+
+ // Check if the argument value is null or empty
+ if (string.IsNullOrEmpty(value))
+ return Task.CompletedTask;
+
+ var model = RestServerUtility.ConvertToScriptHash(value, RestServerPlugin.NeoSystem.Settings);
+ bindingContext.Result = ModelBindingResult.Success(model);
+ }
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/RestServer/Binder/UInt160BinderProvider.cs b/src/RestServer/Binder/UInt160BinderProvider.cs
new file mode 100644
index 000000000..f2aceeb1e
--- /dev/null
+++ b/src/RestServer/Binder/UInt160BinderProvider.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
+
+namespace Neo.Plugins.RestServer.Binder
+{
+ internal class NeoBinderProvider : IModelBinderProvider
+ {
+ public IModelBinder GetBinder(ModelBinderProviderContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (context.Metadata.ModelType == typeof(UInt160))
+ {
+ return new BinderTypeModelBinder(typeof(UInt160Binder));
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/RestServer/Controllers/v1/ContractsController.cs b/src/RestServer/Controllers/v1/ContractsController.cs
new file mode 100644
index 000000000..9c545da9e
--- /dev/null
+++ b/src/RestServer/Controllers/v1/ContractsController.cs
@@ -0,0 +1,214 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Extensions;
+using Neo.Plugins.RestServer.Helpers;
+using Neo.Plugins.RestServer.Models;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.SmartContract;
+using Neo.SmartContract.Manifest;
+using Neo.SmartContract.Native;
+using System.Net.Mime;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ [Route("/api/v{version:apiVersion}/contracts")]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class ContractsController : ControllerBase
+ {
+ private readonly NeoSystem _neosystem;
+
+ public ContractsController()
+ {
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ ///
+ /// Get all the smart contracts from the blockchain.
+ ///
+ /// Page
+ /// Page Size
+ /// An array of Contract object.
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet(Name = "GetContracts")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ContractState[]))]
+ public IActionResult Get(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ var contracts = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
+ if (contracts.Any() == false)
+ return NoContent();
+ var contractRequestList = contracts.OrderBy(o => o.Manifest.Name).Skip((skip - 1) * take).Take(take);
+ if (contractRequestList.Any() == false)
+ return NoContent();
+ return Ok(contractRequestList);
+ }
+
+ ///
+ /// Gets count of total smart contracts on blockchain.
+ ///
+ /// Count Object
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("count", Name = "GetContractCount")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CountModel))]
+ public IActionResult GetCount()
+ {
+ var contracts = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
+ return Ok(new CountModel() { Count = contracts.Count() });
+ }
+
+ ///
+ /// Get a smart contract's storage.
+ ///
+ /// ScriptHash
+ /// An array of the Key (Base64) Value (Base64) Pairs objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}/storage", Name = "GetContractStorage")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(KeyValuePair, ReadOnlyMemory>[]))]
+ public IActionResult GetContractStorage(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+ {
+ if (NativeContract.IsNative(scripthash))
+ return NoContent();
+ var contract = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contract == null)
+ throw new ContractNotFoundException(scripthash);
+ var contractStorage = contract.GetStorage(_neosystem.StoreView);
+ return Ok(contractStorage.Select(s => new KeyValuePair, ReadOnlyMemory>(s.key.Key, s.value.Value)));
+ }
+
+ ///
+ /// Get a smart contract.
+ ///
+ /// ScriptHash
+ /// Contract Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}", Name = "GetContract")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ContractState))]
+ public IActionResult GetByScriptHash(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+ {
+ var contracts = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contracts == null)
+ throw new ContractNotFoundException(scripthash);
+ return Ok(contracts);
+ }
+
+ ///
+ /// Get abi of a smart contract.
+ ///
+ /// ScriptHash
+ /// Contract Abi Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}/abi", Name = "GetContractAbi")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ContractAbi))]
+ public IActionResult GetContractAbi(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+ {
+ var contracts = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contracts == null)
+ throw new ContractNotFoundException(scripthash);
+ return Ok(contracts.Manifest.Abi);
+ }
+ ///
+ /// Get manifest of a smart contract.
+ ///
+ /// ScriptHash
+ /// Contract Manifest object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}/manifest", Name = "GetContractManifest")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ContractManifest))]
+ public IActionResult GetContractManifest(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+ {
+ var contracts = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contracts == null)
+ throw new ContractNotFoundException(scripthash);
+ return Ok(contracts.Manifest);
+ }
+ ///
+ /// Get nef of a smart contract.
+ ///
+ /// ScriptHash
+ /// Contract Nef object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}/nef", Name = "GetContractNefFile")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(NefFile))]
+ public IActionResult GetContractNef(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash)
+ {
+ var contracts = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contracts == null)
+ throw new ContractNotFoundException(scripthash);
+ return Ok(contracts.Nef);
+ }
+
+ ///
+ /// Invoke a method as ReadOnly Flag on a smart contract.
+ ///
+ /// ScriptHash
+ /// method name
+ /// JArray of the contract parameters.
+ /// Execution Engine object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{hash:required}/invoke", Name = "InvokeContractMethod")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ExecutionEngineModel))]
+ public IActionResult InvokeContract(
+ [FromRoute(Name = "hash")]
+ UInt160 scripthash,
+ [FromQuery(Name = "method")]
+ string method,
+ [FromBody]
+ ContractParameter[] contractParameters)
+ {
+ var contracts = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, scripthash);
+ if (contracts == null)
+ throw new ContractNotFoundException(scripthash);
+ if (string.IsNullOrEmpty(method))
+ throw new QueryParameterNotFoundException(nameof(method));
+ try
+ {
+ var engine = ScriptHelper.InvokeMethod(_neosystem.Settings, _neosystem.StoreView, contracts.Hash, method, contractParameters, out var script);
+ return Ok(engine.ToModel());
+ }
+ catch (Exception ex)
+ {
+ throw ex?.InnerException ?? ex;
+ }
+ }
+ }
+}
diff --git a/src/RestServer/Controllers/v1/LedgerController.cs b/src/RestServer/Controllers/v1/LedgerController.cs
new file mode 100644
index 000000000..31d669f74
--- /dev/null
+++ b/src/RestServer/Controllers/v1/LedgerController.cs
@@ -0,0 +1,389 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.Network.P2P.Payloads;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Extensions;
+using Neo.Plugins.RestServer.Models.Blockchain;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Ledger;
+using Neo.SmartContract.Native;
+using System.Net.Mime;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ [Route("/api/v{version:apiVersion}/ledger")]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class LedgerController : ControllerBase
+ {
+ private readonly NeoSystem _neosystem;
+
+ public LedgerController()
+ {
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ #region Accounts
+
+ ///
+ /// Gets all the accounts that hold gas on the blockchain.
+ ///
+ /// An array of account details object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("gas/accounts", Name = "GetGasAccounts")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AccountDetails[]))]
+ public IActionResult ShowGasAccounts()
+ {
+ var accounts = NativeContract.GAS.ListAccounts(_neosystem.StoreView, _neosystem.Settings);
+ return Ok(accounts.OrderByDescending(o => o.Balance));
+ }
+
+ ///
+ /// Get all the accounts that hold neo on the blockchain.
+ ///
+ /// An array of account details object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("neo/accounts", Name = "GetNeoAccounts")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AccountDetails[]))]
+ public IActionResult ShowNeoAccounts()
+ {
+ var accounts = NativeContract.NEO.ListAccounts(_neosystem.StoreView, _neosystem.Settings);
+ return Ok(accounts.OrderByDescending(o => o.Balance));
+ }
+
+ #endregion
+
+ #region Blocks
+
+ ///
+ /// Get blocks from the blockchain.
+ ///
+ /// Page
+ /// Page Size
+ /// An array of Block Header Objects
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks", Name = "GetBlocks")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Header[]))]
+ public IActionResult GetBlocks(
+ [FromQuery(Name = "page")]
+ uint skip = 1,
+ [FromQuery(Name = "size")]
+ uint take = 1)
+ {
+ if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ //var start = (skip - 1) * take + startIndex;
+ //var end = start + take;
+ var start = NativeContract.Ledger.CurrentIndex(_neosystem.StoreView) - (skip - 1) * take;
+ var end = start - take;
+ var lstOfBlocks = new List();
+ for (uint i = start; i > end; i--)
+ {
+ var block = NativeContract.Ledger.GetBlock(_neosystem.StoreView, i);
+ if (block == null)
+ break;
+ lstOfBlocks.Add(block.Header);
+ }
+ if (lstOfBlocks.Any() == false)
+ return NoContent();
+ return Ok(lstOfBlocks);
+ }
+
+ ///
+ /// Gets the current block height of the connected node.
+ ///
+ /// Full Block Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks/height", Name = "GetBlockHeight")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Block))]
+ public IActionResult GetCurrentBlock()
+ {
+ var currentIndex = NativeContract.Ledger.CurrentIndex(_neosystem.StoreView);
+ var block = NativeContract.Ledger.GetHeader(_neosystem.StoreView, currentIndex);
+ return Ok(block);
+ }
+
+ ///
+ /// Gets a block by an its index.
+ ///
+ /// Block Index
+ /// Full Block Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks/{index:min(0)}", Name = "GetBlock")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Block))]
+ public IActionResult GetBlock(
+ [FromRoute(Name = "index")]
+ uint blockIndex)
+ {
+ var block = NativeContract.Ledger.GetBlock(_neosystem.StoreView, blockIndex);
+ if (block == null)
+ throw new BlockNotFoundException(blockIndex);
+ return Ok(block);
+ }
+
+ ///
+ /// Gets a block header by block index.
+ ///
+ /// Blocks index.
+ /// Block Header Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks/{index:min(0)}/header", Name = "GetBlockHeader")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Header))]
+ public IActionResult GetBlockHeader(
+ [FromRoute(Name = "index")]
+ uint blockIndex)
+ {
+ var block = NativeContract.Ledger.GetBlock(_neosystem.StoreView, blockIndex);
+ if (block == null)
+ throw new BlockNotFoundException(blockIndex);
+ return Ok(block.Header);
+ }
+
+ ///
+ /// Gets the witness of the block
+ ///
+ /// Block Index.
+ /// Witness Object
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks/{index:min(0)}/witness", Name = "GetBlockWitness")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Witness))]
+ public IActionResult GetBlockWitness(
+ [FromRoute(Name = "index")]
+ uint blockIndex)
+ {
+ var block = NativeContract.Ledger.GetBlock(_neosystem.StoreView, blockIndex);
+ if (block == null)
+ throw new BlockNotFoundException(blockIndex);
+ return Ok(block.Witness);
+ }
+
+ ///
+ /// Gets the transactions of the block.
+ ///
+ /// Block Index.
+ /// Page
+ /// Page Size
+ /// An array of transaction object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("blocks/{index:min(0)}/transactions", Name = "GetBlockTransactions")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction[]))]
+ public IActionResult GetBlockTransactions(
+ [FromRoute(Name = "index")]
+ uint blockIndex,
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ var block = NativeContract.Ledger.GetBlock(_neosystem.StoreView, blockIndex);
+ if (block == null)
+ throw new BlockNotFoundException(blockIndex);
+ if (block.Transactions == null || block.Transactions.Length == 0)
+ return NoContent();
+ return Ok(block.Transactions.Skip((skip - 1) * take).Take(take));
+ }
+
+ #endregion
+
+ #region Transactions
+
+ ///
+ /// Gets a transaction
+ ///
+ /// Hash256
+ /// Transaction object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("transactions/{hash:required}", Name = "GetTransaction")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction))]
+ public IActionResult GetTransaction(
+ [FromRoute( Name = "hash")]
+ UInt256 hash)
+ {
+ if (NativeContract.Ledger.ContainsTransaction(_neosystem.StoreView, hash) == false)
+ return NotFound();
+ var txst = NativeContract.Ledger.GetTransaction(_neosystem.StoreView, hash);
+ if (txst == null)
+ throw new TransactionNotFoundException(hash);
+ return Ok(txst);
+ }
+
+ ///
+ /// Gets the witness of a transaction.
+ ///
+ /// Hash256
+ /// An array of witness object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("transactions/{hash:required}/witnesses", Name = "GetTransactionWitnesses")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Witness[]))]
+ public IActionResult GetTransactionWitnesses(
+ [FromRoute( Name = "hash")]
+ UInt256 hash)
+ {
+ if (NativeContract.Ledger.ContainsTransaction(_neosystem.StoreView, hash) == false)
+ throw new TransactionNotFoundException(hash);
+ var tx = NativeContract.Ledger.GetTransaction(_neosystem.StoreView, hash);
+ return Ok(tx.Witnesses);
+ }
+
+ ///
+ /// Gets the signers of a transaction.
+ ///
+ /// Hash256
+ /// An array of Signer object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("transactions/{hash:required}/signers", Name = "GetTransactionSigners")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Signer[]))]
+ public IActionResult GetTransactionSigners(
+ [FromRoute( Name = "hash")]
+ UInt256 hash)
+ {
+ if (NativeContract.Ledger.ContainsTransaction(_neosystem.StoreView, hash) == false)
+ throw new TransactionNotFoundException(hash);
+ var tx = NativeContract.Ledger.GetTransaction(_neosystem.StoreView, hash);
+ return Ok(tx.Signers);
+ }
+
+ ///
+ /// Gets the transaction attributes of a transaction.
+ ///
+ /// Hash256
+ /// An array of the transaction attributes object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("transactions/{hash:required}/attributes", Name = "GetTransactionAttributes")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(TransactionAttribute[]))]
+ public IActionResult GetTransactionAttributes(
+ [FromRoute( Name = "hash")]
+ UInt256 hash)
+ {
+ if (NativeContract.Ledger.ContainsTransaction(_neosystem.StoreView, hash) == false)
+ throw new TransactionNotFoundException(hash);
+ var tx = NativeContract.Ledger.GetTransaction(_neosystem.StoreView, hash);
+ return Ok(tx.Attributes);
+ }
+
+ #endregion
+
+ #region Memory Pool
+
+ ///
+ /// Gets memory pool.
+ ///
+ /// Page
+ /// Page Size.
+ /// An array of the Transaction object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("memorypool", Name = "GetMemoryPoolTransactions")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction[]))]
+ public IActionResult GetMemoryPool(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 0 || take < 0 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ return Ok(_neosystem.MemPool.Skip((skip - 1) * take).Take(take));
+ }
+
+ ///
+ /// Gets the count of the memory pool.
+ ///
+ /// Memory Pool Count Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("memorypool/count", Name = "GetMemoryPoolCount")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MemoryPoolCountModel))]
+ public IActionResult GetMemoryPoolCount() =>
+ Ok(new MemoryPoolCountModel()
+ {
+ Count = _neosystem.MemPool.Count,
+ UnVerifiedCount = _neosystem.MemPool.UnVerifiedCount,
+ VerifiedCount = _neosystem.MemPool.VerifiedCount,
+ });
+
+ ///
+ /// Gets verified memory pool.
+ ///
+ /// Page
+ /// Page Size.
+ /// An array of the Transaction object.
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("memorypool/verified", Name = "GetMemoryPoolVeridiedTransactions")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction[]))]
+ public IActionResult GetMemoryPoolVerified(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 0 || take < 0 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ if (_neosystem.MemPool.Any() == false)
+ return NoContent();
+ var vTx = _neosystem.MemPool.GetVerifiedTransactions();
+ return Ok(vTx.Skip((skip - 1) * take).Take(take));
+ }
+
+ ///
+ /// Gets unverified memory pool.
+ ///
+ /// Page
+ /// Page Size.
+ /// An array of the Transaction object.
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("memorypool/unverified", Name = "GetMemoryPoolUnveridiedTransactions")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction[]))]
+ public IActionResult GetMemoryPoolUnVerified(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 0 || take < 0 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ if (_neosystem.MemPool.Any() == false)
+ return NoContent();
+ _neosystem.MemPool.GetVerifiedAndUnverifiedTransactions(out _, out var unVerifiedTransactions);
+ return Ok(unVerifiedTransactions.Skip((skip - 1) * take).Take(take));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/RestServer/Controllers/v1/NodeController.cs b/src/RestServer/Controllers/v1/NodeController.cs
new file mode 100644
index 000000000..2bde367cc
--- /dev/null
+++ b/src/RestServer/Controllers/v1/NodeController.cs
@@ -0,0 +1,86 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.IO;
+using Neo.Network.P2P;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Extensions;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Node;
+using System.Net.Mime;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ [Route("/api/v{version:apiVersion}/node")]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class NodeController : ControllerBase
+ {
+ private readonly LocalNode _neolocalnode;
+ private readonly NeoSystem _neosystem;
+
+ public NodeController()
+ {
+ _neolocalnode = RestServerPlugin.LocalNode;
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ ///
+ /// Gets the connected remote nodes.
+ ///
+ /// An array of the Remote Node Objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("peers", Name = "GetNodeRemotePeers")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(RemoteNodeModel[]))]
+ public IActionResult GetPeers()
+ {
+ var rNodes = _neolocalnode
+ .GetRemoteNodes()
+ .OrderByDescending(o => o.LastBlockIndex)
+ .ToArray();
+
+ return Ok(rNodes.Select(s => s.ToModel()));
+ }
+
+ ///
+ /// Gets all the loaded plugins of the current connected node.
+ ///
+ /// An array of the Plugin objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("plugins", Name = "GetNodePlugins")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(PluginModel[]))]
+ public IActionResult GetPlugins() =>
+ Ok(Plugin.Plugins.Select(s =>
+ new PluginModel()
+ {
+ Name = s.Name,
+ Version = s.Version.ToString(3),
+ Description = s.Description,
+ }));
+
+ ///
+ /// Gets the ProtocolSettings of the currently connected node.
+ ///
+ /// Protocol Settings Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("settings", Name = "GetNodeProtocolSettings")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ProtocolSettingsModel))]
+ public IActionResult GetSettings() =>
+ Ok(_neosystem.Settings.ToModel());
+ }
+}
diff --git a/src/RestServer/Controllers/v1/TokensController.cs b/src/RestServer/Controllers/v1/TokensController.cs
new file mode 100644
index 000000000..a947d78cd
--- /dev/null
+++ b/src/RestServer/Controllers/v1/TokensController.cs
@@ -0,0 +1,304 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Extensions;
+using Neo.Plugins.RestServer.Helpers;
+using Neo.Plugins.RestServer.Models;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Token;
+using Neo.Plugins.RestServer.Tokens;
+using Neo.SmartContract.Native;
+using System.Net.Mime;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ [Route("/api/v{version:apiVersion}/tokens")]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class TokensController : ControllerBase
+ {
+ private readonly NeoSystem _neosystem;
+
+ public TokensController()
+ {
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ #region NEP-17
+
+ ///
+ /// Gets all Nep-17 valid contracts from the blockchain.
+ ///
+ /// Page
+ /// Page Size
+ /// An array of the Nep-17 Token Object.
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-17", Name = "GetNep17Tokens")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(NEP17TokenModel[]))]
+ public IActionResult GetNEP17(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
+ var vaildContracts = tokenList
+ .Where(w => ContractHelper.IsNep17Supported(w))
+ .OrderBy(o => o.Manifest.Name)
+ .Skip((skip - 1) * take)
+ .Take(take);
+ if (vaildContracts.Any() == false)
+ return NoContent();
+ var listResults = new List();
+ foreach (var contract in vaildContracts)
+ {
+ try
+ {
+ var token = new NEP17Token(_neosystem, contract.Hash);
+ listResults.Add(token.ToModel());
+ }
+ catch
+ {
+ }
+ }
+ if (listResults.Any() == false)
+ return NoContent();
+ return Ok(listResults);
+ }
+
+ ///
+ /// The count of how many Nep-17 contracts are on the blockchain.
+ ///
+ /// Count Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-17/count", Name = "GetNep17TokenCount")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CountModel))]
+ public IActionResult GetNEP17Count()
+ {
+ return Ok(new CountModel()
+ {
+ Count = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView).Count(c => ContractHelper.IsNep17Supported(c))
+ });
+ }
+
+ ///
+ /// Gets the balance of the Nep-17 contract by an address.
+ ///
+ /// Nep-17 ScriptHash
+ /// Neo Address ScriptHash
+ /// Token Balance Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-17/{scripthash:required}/balanceof/{address:required}", Name = "GetNep17TokenBalanceOf")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(TokenBalanceModel))]
+ public IActionResult GetNEP17(
+ [FromRoute(Name = "scripthash")]
+ UInt160 tokenAddessOrScripthash,
+ [FromRoute(Name = "address")]
+ UInt160 lookupAddressOrScripthash)
+ {
+ var contract = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, tokenAddessOrScripthash);
+ if (contract == null)
+ throw new ContractNotFoundException(tokenAddessOrScripthash);
+ if (ContractHelper.IsNep17Supported(contract) == false)
+ throw new Nep17NotSupportedException(tokenAddessOrScripthash);
+ try
+ {
+ var token = new NEP17Token(_neosystem, tokenAddessOrScripthash);
+ return Ok(new TokenBalanceModel()
+ {
+ Name = token.Name,
+ ScriptHash = token.ScriptHash,
+ Symbol = token.Symbol,
+ Decimals = token.Decimals,
+ Balance = token.BalanceOf(lookupAddressOrScripthash).Value,
+ TotalSupply = token.TotalSupply().Value,
+ });
+ }
+ catch
+ {
+ throw new Nep17NotSupportedException(tokenAddessOrScripthash);
+ }
+ }
+
+ #endregion
+
+ #region NEP-11
+
+ ///
+ /// Gets all the Nep-11 valid contracts on from the blockchain.
+ ///
+ /// Page
+ /// Page Size
+ /// Nep-11 Token Object.
+ /// No more pages.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-11", Name = "GetNep11Tokens")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(NEP11TokenModel[]))]
+ public IActionResult GetNEP11(
+ [FromQuery(Name = "page")]
+ int skip = 1,
+ [FromQuery(Name = "size")]
+ int take = 1)
+ {
+ if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize)
+ throw new InvalidParameterRangeException();
+ var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
+ var vaildContracts = tokenList
+ .Where(ContractHelper.IsNep11Supported)
+ .OrderBy(o => o.Manifest.Name)
+ .Skip((skip - 1) * take)
+ .Take(take);
+ if (vaildContracts.Any() == false)
+ return NoContent();
+ var listResults = new List();
+ foreach (var contract in vaildContracts)
+ {
+ try
+ {
+ var token = new NEP11Token(_neosystem, contract.Hash);
+ listResults.Add(token.ToModel());
+ }
+ catch
+ {
+ }
+ }
+ if (listResults.Any() == false)
+ return NoContent();
+ return Ok(listResults);
+ }
+
+ ///
+ /// The count of how many Nep-11 contracts are on the blockchain.
+ ///
+ /// Count Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-11/count", Name = "GetNep11TokenCount")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CountModel))]
+ public IActionResult GetNEP11Count()
+ {
+ return Ok(new CountModel() { Count = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView).Count(c => ContractHelper.IsNep11Supported(c)) });
+ }
+
+ ///
+ /// Gets the balance of the Nep-11 contract by an address.
+ ///
+ /// Nep-11 ScriptHash
+ /// Neo Address ScriptHash
+ /// Token Balance Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("nep-11/{scripthash:required}/balanceof/{address:required}", Name = "GetNep11TokenBalanceOf")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(TokenBalanceModel))]
+ public IActionResult GetNEP11(
+ [FromRoute(Name = "scripthash")]
+ UInt160 sAddressHash,
+ [FromRoute(Name = "address")]
+ UInt160 addressHash)
+ {
+ var contract = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, sAddressHash);
+ if (contract == null)
+ throw new ContractNotFoundException(sAddressHash);
+ if (ContractHelper.IsNep11Supported(contract) == false)
+ throw new Nep11NotSupportedException(sAddressHash);
+ try
+ {
+ var token = new NEP11Token(_neosystem, sAddressHash);
+ return Ok(new TokenBalanceModel()
+ {
+ Name = token.Name,
+ ScriptHash = token.ScriptHash,
+ Symbol = token.Symbol,
+ Decimals = token.Decimals,
+ Balance = token.BalanceOf(addressHash).Value,
+ TotalSupply = token.TotalSupply().Value,
+ });
+ }
+ catch
+ {
+ throw new Nep11NotSupportedException(sAddressHash);
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Gets every single NEP17/NEP11 on the blockchain's balance by ScriptHash
+ ///
+ ///
+ /// Token Balance Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("balanceof/{address:required}", Name = "GetAllTokensBalanceOf")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(TokenBalanceModel))]
+ public IActionResult GetBalances(
+ [FromRoute(Name = "address")]
+ UInt160 addressOrScripthash)
+ {
+ var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
+ var validContracts = tokenList
+ .Where(w => ContractHelper.IsNep17Supported(w) || ContractHelper.IsNep11Supported(w))
+ .OrderBy(o => o.Manifest.Name);
+ var listResults = new List();
+ foreach (var contract in validContracts)
+ {
+ try
+ {
+ var token = new NEP17Token(_neosystem, contract.Hash);
+ var balance = token.BalanceOf(addressOrScripthash).Value;
+ if (balance == 0)
+ continue;
+ listResults.Add(new()
+ {
+ Name = token.Name,
+ ScriptHash = token.ScriptHash,
+ Symbol = token.Symbol,
+ Decimals = token.Decimals,
+ Balance = balance,
+ TotalSupply = token.TotalSupply().Value,
+ });
+
+ var nft = new NEP11Token(_neosystem, contract.Hash);
+ balance = nft.BalanceOf(addressOrScripthash).Value;
+ if (balance == 0)
+ continue;
+ listResults.Add(new()
+ {
+ Name = nft.Name,
+ ScriptHash = nft.ScriptHash,
+ Symbol = nft.Symbol,
+ Balance = balance,
+ Decimals = nft.Decimals,
+ TotalSupply = nft.TotalSupply().Value,
+ });
+ }
+ catch (NotSupportedException)
+ {
+ }
+ }
+ return Ok(listResults);
+ }
+ }
+}
diff --git a/src/RestServer/Controllers/v1/UtilsController.cs b/src/RestServer/Controllers/v1/UtilsController.cs
new file mode 100644
index 000000000..ad3f54342
--- /dev/null
+++ b/src/RestServer/Controllers/v1/UtilsController.cs
@@ -0,0 +1,106 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Utils;
+using Neo.Wallets;
+using System.Net.Mime;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ [Route("/api/v{version:apiVersion}/utils")]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class UtilsController : ControllerBase
+ {
+ private readonly NeoSystem _neosystem;
+
+ public UtilsController()
+ {
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ #region Validation
+
+ ///
+ /// Converts script to Neo address.
+ ///
+ /// ScriptHash
+ /// Util Address Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{hash:required}/address", Name = "GetAddressByScripthash")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UtilsAddressModel))]
+ public IActionResult ScriptHashToWalletAddress(
+ [FromRoute(Name = "hash")]
+ UInt160 ScriptHash)
+ {
+ try
+ {
+ return Ok(new UtilsAddressModel() { Address = ScriptHash.ToAddress(_neosystem.Settings.AddressVersion) });
+ }
+ catch (FormatException)
+ {
+ throw new ScriptHashFormatException();
+ }
+ }
+
+ ///
+ /// Converts Neo address to ScriptHash
+ ///
+ /// Neo Address
+ /// Util ScriptHash Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{address:required}/scripthash", Name = "GetScripthashByAddress")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UtilsScriptHashModel))]
+ public IActionResult WalletAddressToScriptHash(
+ [FromRoute(Name = "address")]
+ string address)
+ {
+ try
+ {
+ return Ok(new UtilsScriptHashModel() { ScriptHash = address.ToScriptHash(_neosystem.Settings.AddressVersion) });
+ }
+ catch (FormatException)
+ {
+ throw new AddressFormatException();
+ }
+ }
+
+ ///
+ /// Get whether or not a Neo address or ScriptHash is valid.
+ ///
+ ///
+ /// Util Address Valid Object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{address:required}/validate", Name = "IsValidAddressOrScriptHash")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UtilsAddressIsValidModel))]
+ public IActionResult ValidateAddress(
+ [FromRoute(Name = "address")]
+ string AddressOrScriptHash)
+ {
+ return Ok(new UtilsAddressIsValidModel()
+ {
+ Address = AddressOrScriptHash,
+ IsValid = RestServerUtility.TryConvertToScriptHash(AddressOrScriptHash, _neosystem.Settings, out _),
+ });
+ }
+
+ #endregion
+ }
+}
diff --git a/src/RestServer/Controllers/v1/WalletController.cs b/src/RestServer/Controllers/v1/WalletController.cs
new file mode 100644
index 000000000..56760825c
--- /dev/null
+++ b/src/RestServer/Controllers/v1/WalletController.cs
@@ -0,0 +1,646 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Actor;
+using Microsoft.AspNetCore.Cors;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Neo.Network.P2P.Payloads;
+using Neo.Plugins.RestServer.Exceptions;
+using Neo.Plugins.RestServer.Helpers;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Wallet;
+using Neo.SmartContract;
+using Neo.SmartContract.Native;
+using Neo.Wallets;
+using Neo.Wallets.NEP6;
+using System.Net.Mime;
+using System.Numerics;
+
+namespace Neo.Plugins.RestServer.Controllers.v1
+{
+ ///
+ /// Wallet API
+ ///
+ [Route("/api/v{version:apiVersion}/wallet")]
+ [DisableCors]
+ [Produces(MediaTypeNames.Application.Json)]
+ [Consumes(MediaTypeNames.Application.Json)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorModel))]
+ [ApiVersion("1.0")]
+ [ApiController]
+ public class WalletController : ControllerBase
+ {
+ internal static WalletSessionManager WalletSessions { get; } = new();
+
+ private readonly NeoSystem _neosystem;
+
+ ///
+ /// CTOR
+ ///
+ /// Node network doesn't match plugins network.
+ public WalletController()
+ {
+ _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException();
+ }
+
+ ///
+ /// Opens a wallet.
+ ///
+ ///
+ /// A newly created wallet session object.
+ /// Returns newly create wallet session object.
+ /// An error occurred. See Response for details.
+ [HttpPost("open", Name = "WalletOpen")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletSessionModel))]
+ public IActionResult WalletOpen(
+ [FromBody]
+ WalletOpenModel model)
+ {
+ if (new FileInfo(model.Path).DirectoryName.StartsWith(AppContext.BaseDirectory, StringComparison.InvariantCultureIgnoreCase) == false)
+ throw new UnauthorizedAccessException(model.Path);
+ if (System.IO.File.Exists(model.Path) == false)
+ throw new FileNotFoundException(null, model.Path);
+ var wallet = Wallet.Open(model.Path, model.Password, _neosystem.Settings);
+ if (wallet == null)
+ throw new WalletOpenException($"File '{model.Path}' could not be opened.");
+ var sessionId = Guid.NewGuid();
+ WalletSessions[sessionId] = new WalletSession(wallet);
+ return Ok(new WalletSessionModel()
+ {
+ SessionId = sessionId,
+ });
+ }
+
+ ///
+ /// Closes a wallet session.
+ ///
+ /// Session Id of the open/created wallet.
+ /// Empty response body, if successful.
+ /// Successfully closed the wallet session.
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/close", Name = "WalletClose")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public IActionResult WalletClose(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ if (WalletSessions.TryRemove(sessionId, out _) == false)
+ throw new WalletSessionException("Failed to remove session.");
+ return Ok();
+ }
+
+ ///
+ /// Get all the keys of the wallet from each account.
+ ///
+ /// Session Id of the open/created wallet.
+ /// A list of export key objects.
+ /// Successfully exported the keys.
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/export", Name = "WalletExportKeys")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletExportKeyModel[]))]
+ public IActionResult WalletExport(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var keys = wallet.GetAccounts().Where(p => p.HasKey)
+ .Select(s => new WalletExportKeyModel()
+ {
+ ScriptHash = s.ScriptHash,
+ Address = s.Address,
+ Wif = s.GetKey().Export()
+ }).ToArray();
+ return Ok(keys);
+ }
+
+ ///
+ /// Get a key of the wallet by a specific account.
+ ///
+ /// Session Id of the open/created wallet.
+ /// ScriptHash of the wallet address.
+ /// A export key object.
+ /// Successfully exported the key.
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/export/{address:required}", Name = "WalletExportKeysByAddressOrScripthash")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletExportKeyModel))]
+ public IActionResult WalletExportKey(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromRoute(Name = "address")]
+ UInt160 scriptHash)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var account = wallet.GetAccount(scriptHash);
+ if (account == null)
+ throw new WalletException($"Account {scriptHash} doesn\'t exist.");
+ var key = account.GetKey();
+ return Ok(new WalletExportKeyModel
+ {
+ ScriptHash = account.ScriptHash,
+ Address = account.Address,
+ Wif = key.Export(),
+ });
+ }
+
+ ///
+ /// Create a new address in the wallet with an optional private key.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ /// Wallet address object.
+ /// Successfully created address.
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/address/create", Name = "WalletCreateAddress")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAddressModel))]
+ public IActionResult WalletCreateNewAddress(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletCreateAccountModel model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var account = model.PrivateKey == null || model.PrivateKey.Length == 0 ?
+ wallet.CreateAccount() :
+ wallet.CreateAccount(model.PrivateKey);
+ if (account == null)
+ throw new WalletException("Account couldn't be created.");
+ if (wallet is NEP6Wallet nep6)
+ nep6.Save();
+ return Ok(new WalletAddressModel()
+ {
+ Address = account.Address,
+ ScriptHash = account.ScriptHash,
+ Publickey = account.GetKey().PublicKey,
+ HasKey = account.HasKey,
+ Label = account.Label,
+ WatchOnly = account.WatchOnly,
+ });
+ }
+
+ ///
+ /// Get the wallet balance of a specific asset.
+ ///
+ /// Session Id of the open/created wallet.
+ /// ScriptHash of the wallet address.
+ /// Account balance object of all the accounts in the wallet.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/balance/{asset:required}", Name = "WalletBalanceOf")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAccountBalanceModel))]
+ public IActionResult WalletBalance(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromRoute(Name = "asset")]
+ UInt160 scriptHash)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var balance = wallet.GetAvailable(_neosystem.StoreView, scriptHash);
+ return Ok(new WalletAccountBalanceModel()
+ {
+ Balance = balance.Value,
+ });
+ }
+
+ ///
+ /// Get unclaimed gas of the wallet for all accounts total.
+ ///
+ /// Session Id of the open/created wallet.
+ /// Account balance object
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/gas/unclaimed", Name = "GetUnClaimedGas")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAccountBalanceModel))]
+ public IActionResult WalletUnClaimedGas(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ using var snapshot = _neosystem.GetSnapshot();
+ uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1;
+ BigInteger gas = BigInteger.Zero;
+ foreach (var account in wallet.GetAccounts().Select(s => s.ScriptHash))
+ gas += NativeContract.NEO.UnclaimedGas(snapshot, account, height);
+ return Ok(new WalletAccountBalanceModel
+ {
+ Balance = gas,
+ });
+ }
+
+ ///
+ /// import a private key into the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ /// New wallet address object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/import", Name = "WalletImportByWif")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAddressModel))]
+ public IActionResult WalletImportPrivateKey(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletImportKey model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var account = wallet.Import(model.Wif);
+ if (account == null)
+ throw new WalletException("Account couldn\'t be imported.");
+ if (wallet is NEP6Wallet nep6)
+ nep6.Save();
+ return Ok(new WalletAddressModel()
+ {
+ Address = account.Address,
+ ScriptHash = account.ScriptHash,
+ Publickey = account.GetKey().PublicKey,
+ HasKey = account.HasKey,
+ Label = account.Label,
+ WatchOnly = account.WatchOnly,
+ });
+ }
+
+ ///
+ /// List all the addresses in the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ /// An array of wallet address objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/address/list", Name = "GetWalletListAddress")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAddressModel[]))]
+ public IActionResult WalletListAddresses(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var accounts = new List();
+ foreach (var account in wallet.GetAccounts())
+ accounts.Add(new WalletAddressModel()
+ {
+ Address = account.Address,
+ ScriptHash = account.ScriptHash,
+ Publickey = account.GetKey().PublicKey,
+ HasKey = account.HasKey,
+ Label = account.Label,
+ WatchOnly = account.WatchOnly,
+ });
+ return Ok(accounts);
+ }
+
+ ///
+ /// Deletes an account from the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ /// ScriptHash of the wallet address.
+ /// Empty body response.
+ /// No backups are made.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/delete/{account:required}", Name = "WalletDeleteAccountByAddressOrScriptHash")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public IActionResult WalletDeleteAccount(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromRoute(Name = "account")]
+ UInt160 scriptHash)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ if (wallet.DeleteAccount(scriptHash) == false)
+ throw new WalletException($"Could not delete '{scriptHash}' account.");
+ else
+ {
+ if (wallet is NEP6Wallet wallet6)
+ wallet6.Save();
+ return Ok();
+ }
+ }
+
+ ///
+ /// Trasnsfer assets from one wallet address to another address on the blockchain.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ /// Transaction object
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/transfer", Name = "WalletTransferAssets")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction))]
+ public IActionResult WalletTransferAssets(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletSendModel model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ using var snapshot = _neosystem.GetSnapshot();
+ try
+ {
+ var descriptor = new AssetDescriptor(snapshot, _neosystem.Settings, model.AssetId);
+ var amount = new BigDecimal(model.Amount, descriptor.Decimals);
+ if (amount.Sign <= 0)
+ throw new WalletException($"Invalid Amount.");
+ var signers = model.Signers?.Select(s => new Signer() { Scopes = WitnessScope.CalledByEntry, Account = s }).ToArray();
+ var tx = wallet.MakeTransaction(snapshot,
+ new[]
+ {
+ new TransferOutput()
+ {
+ AssetId = model.AssetId,
+ Value = amount,
+ ScriptHash = model.To,
+ Data = model.Data,
+ },
+ },
+ model.From, signers);
+ if (tx == null)
+ throw new WalletInsufficientFundsException();
+ var totalFees = new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals);
+ if (totalFees.Value > RestServerSettings.Current.MaxTransactionFee)
+ throw new WalletException("The transaction fees are to much.");
+ var context = new ContractParametersContext(snapshot, tx, _neosystem.Settings.Network);
+ wallet.Sign(context);
+ if (context.Completed == false)
+ throw new WalletException("Transaction could not be completed at this time.");
+ tx.Witnesses = context.GetWitnesses();
+ _neosystem.Blockchain.Tell(tx);
+ return Ok(tx);
+ }
+ catch (Exception ex)
+ {
+ throw ex.InnerException ?? ex;
+ }
+ }
+
+ ///
+ /// Create a wallet.
+ ///
+ ///
+ /// A wallet session object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("create", Name = "WalletCreate")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletSessionModel))]
+ public IActionResult WalletCreate(
+ [FromBody]
+ WalletCreateModel model)
+ {
+ if (new FileInfo(model.Path).DirectoryName.StartsWith(AppContext.BaseDirectory, StringComparison.InvariantCultureIgnoreCase) == false)
+ throw new UnauthorizedAccessException(model.Path);
+ var wallet = Wallet.Create(model.Name, model.Path, model.Password, _neosystem.Settings);
+ if (wallet == null)
+ throw new WalletException("Wallet files in that format are not supported, please use a .json or .db3 file extension.");
+ if (string.IsNullOrEmpty(model.Wif) == false)
+ wallet.Import(model.Wif);
+ if (wallet is NEP6Wallet nep6)
+ nep6.Save();
+ var sessionId = Guid.NewGuid();
+ WalletSessions[sessionId] = new WalletSession(wallet);
+ return Ok(new WalletSessionModel()
+ {
+ SessionId = sessionId,
+ });
+ }
+
+ ///
+ /// Import multi-signature addresss into the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ ///
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/import/multisigaddress", Name = "WalletImportMultiSigAddress")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletMultiSignContractModel))]
+ public IActionResult WalletImportMultiSigAddress(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletImportMultiSigAddressModel model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ if (model.PublicKeys == null || model.PublicKeys.Length == 0)
+ throw new WalletException($"{nameof(model.PublicKeys)} is invalid.");
+ var session = WalletSessions[sessionId];
+ var wallet = session.Wallet;
+ session.ResetExpiration();
+
+ int n = model.PublicKeys.Length;
+
+ if (model.RequiredSignatures < 1 || model.RequiredSignatures > n || n > 1024)
+ throw new WalletException($"{nameof(model.RequiredSignatures)} and {nameof(model.PublicKeys)} is invalid.");
+
+ Contract multiSignContract = Contract.CreateMultiSigContract(model.RequiredSignatures, model.PublicKeys);
+ KeyPair keyPair = wallet.GetAccounts().FirstOrDefault(p => p.HasKey && model.PublicKeys.Contains(p.GetKey().PublicKey))?.GetKey();
+ if (keyPair == null)
+ throw new WalletException("Couldn\'t get key pair.");
+ var account = wallet.CreateAccount(multiSignContract, keyPair);
+ if (account == null)
+ throw new WalletException("Account couldn\'t be created.");
+ if (wallet is NEP6Wallet nep6)
+ nep6.Save();
+ return Ok(new WalletMultiSignContractModel
+ {
+ Address = multiSignContract.ScriptHash.ToAddress(_neosystem.Settings.AddressVersion),
+ ScriptHash = multiSignContract.ScriptHash,
+ Script = multiSignContract.Script,
+ });
+ }
+
+ ///
+ /// List assets of the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ /// An array of wallet asset objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/asset/list", Name = "GetWalletAssetList")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletAssetModel[]))]
+ public IActionResult WalletListAsset(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ var wallet = session.Wallet;
+ session.ResetExpiration();
+ var assets = new List();
+ foreach (var account in wallet.GetAccounts())
+ assets.Add(new()
+ {
+ Address = account.Address,
+ ScriptHash = account.ScriptHash,
+ PublicKey = account.GetKey().PublicKey,
+ Neo = wallet.GetBalance(_neosystem.StoreView, NativeContract.NEO.Hash, account.ScriptHash).Value,
+ NeoHash = NativeContract.NEO.Hash,
+ Gas = wallet.GetBalance(_neosystem.StoreView, NativeContract.GAS.Hash, account.ScriptHash).Value,
+ GasHash = NativeContract.GAS.Hash,
+ });
+ return Ok(assets);
+ }
+
+ ///
+ /// List all account keys in the wallet.
+ ///
+ /// Session Id of the open/created wallet.
+ /// An array of wallet key objects.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpGet("{session:required}/key/list", Name = "GetWalletKeyList")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WalletKeyModel[]))]
+ public IActionResult WalletListKeys(
+ [FromRoute(Name = "session")]
+ Guid sessionId)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ var session = WalletSessions[sessionId];
+ var wallet = session.Wallet;
+ session.ResetExpiration();
+ var keys = new List();
+ foreach (var account in wallet.GetAccounts().Where(w => w.HasKey))
+ keys.Add(new()
+ {
+ Address = account.Address,
+ ScriptHash = account.ScriptHash,
+ PublicKey = account.GetKey().PublicKey,
+ });
+ return Ok(keys);
+ }
+
+ ///
+ /// Change wallet password.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ /// Empty body response.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/changepassword", Name = "WalletChangePassword")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public IActionResult WalletChangePassword(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletChangePasswordModel model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ if (model.OldPassword == model.NewPassword)
+ throw new WalletException($"{nameof(model.OldPassword)} is the same as {nameof(model.NewPassword)}.");
+ var session = WalletSessions[sessionId];
+ var wallet = session.Wallet;
+ session.ResetExpiration();
+ if (wallet.VerifyPassword(model.OldPassword) == false)
+ throw new WalletException("Invalid password! Session terminated!");
+ if (model.CreateBackupFile && wallet is NEP6Wallet)
+ {
+ var bakFile = wallet.Path + $".{sessionId:n}.bak";
+ if (System.IO.File.Exists(wallet.Path) == false)
+ throw new WalletException("Create wallet backup failed. Wallet file doesn\'t exist.");
+ if (System.IO.File.Exists(bakFile) && model.OverwriteIfBackupFileExists == false)
+ throw new WalletException("Backup File already exists for this session.");
+ System.IO.File.Copy(wallet.Path, bakFile, true);
+ }
+ if (wallet.ChangePassword(model.OldPassword, model.NewPassword) == false)
+ throw new WalletException("Failed to change password!");
+ if (wallet is NEP6Wallet nep6)
+ nep6.Save();
+ return Ok();
+ }
+
+ ///
+ /// Create a transaction with a script.
+ ///
+ /// Session Id of the open/created wallet.
+ ///
+ /// Transaction object.
+ /// Successful
+ /// An error occurred. See Response for details.
+ [HttpPost("{session:required}/transaction/script", Name = "WalletTransactionWithScript")]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Transaction))]
+ public IActionResult WalletTransactionWithScript(
+ [FromRoute(Name = "session")]
+ Guid sessionId,
+ [FromBody]
+ WalletTransactionScriptModel model)
+ {
+ if (WalletSessions.ContainsKey(sessionId) == false)
+ throw new KeyNotFoundException(sessionId.ToString("n"));
+ if (model.Script == null || model.Script.Length == 0)
+ throw new JsonPropertyNullOrEmptyException(nameof(model.Script));
+ var session = WalletSessions[sessionId];
+ session.ResetExpiration();
+ var wallet = session.Wallet;
+ var signers = model.Signers?.Select(s => new Signer() { Scopes = WitnessScope.CalledByEntry, Account = s }).ToArray();
+ var appEngine = ScriptHelper.InvokeScript(model.Script, signers);
+ if (appEngine.State != VM.VMState.HALT)
+ throw new ApplicationEngineException(appEngine.FaultException?.InnerException?.Message ?? appEngine.FaultException?.Message ?? string.Empty);
+ var tx = wallet.MakeTransaction(_neosystem.StoreView, model.Script, model.From, signers, maxGas: RestServerSettings.Current.MaxGasInvoke);
+ try
+ {
+ var context = new ContractParametersContext(_neosystem.StoreView, tx, _neosystem.Settings.Network);
+ wallet.Sign(context);
+ if (context.Completed == false)
+ throw new WalletException($"Incomplete signature: {context}");
+ else
+ {
+ tx.Witnesses = context.GetWitnesses();
+ _neosystem.Blockchain.Tell(tx);
+ return Ok(tx);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw ex.InnerException ?? ex;
+ }
+ }
+ }
+}
diff --git a/src/RestServer/Exceptions/AddressFormatException.cs b/src/RestServer/Exceptions/AddressFormatException.cs
new file mode 100644
index 000000000..e2249ac36
--- /dev/null
+++ b/src/RestServer/Exceptions/AddressFormatException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class AddressFormatException : Exception
+ {
+ public AddressFormatException() : base() { }
+ public AddressFormatException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/ApplicationEngineException.cs b/src/RestServer/Exceptions/ApplicationEngineException.cs
new file mode 100644
index 000000000..37030f7e6
--- /dev/null
+++ b/src/RestServer/Exceptions/ApplicationEngineException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class ApplicationEngineException : Exception
+ {
+ public ApplicationEngineException() : base() { }
+ public ApplicationEngineException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/BlockNotFoundException.cs b/src/RestServer/Exceptions/BlockNotFoundException.cs
new file mode 100644
index 000000000..c669afcf5
--- /dev/null
+++ b/src/RestServer/Exceptions/BlockNotFoundException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class BlockNotFoundException : Exception
+ {
+ public BlockNotFoundException() { }
+ public BlockNotFoundException(uint index) : base($"block '{index}' as not found.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/ContractNotFoundException.cs b/src/RestServer/Exceptions/ContractNotFoundException.cs
new file mode 100644
index 000000000..f363c467e
--- /dev/null
+++ b/src/RestServer/Exceptions/ContractNotFoundException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class ContractNotFoundException : Exception
+ {
+ public ContractNotFoundException() : base() { }
+ public ContractNotFoundException(UInt160 scriptHash) : base($"Contract '{scriptHash}' was not found.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/InvalidParameterRangeException.cs b/src/RestServer/Exceptions/InvalidParameterRangeException.cs
new file mode 100644
index 000000000..021e40cf5
--- /dev/null
+++ b/src/RestServer/Exceptions/InvalidParameterRangeException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class InvalidParameterRangeException : Exception
+ {
+ public InvalidParameterRangeException() : base() { }
+ public InvalidParameterRangeException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/JsonPropertyNullOrEmptyException.cs b/src/RestServer/Exceptions/JsonPropertyNullOrEmptyException.cs
new file mode 100644
index 000000000..6afe08e79
--- /dev/null
+++ b/src/RestServer/Exceptions/JsonPropertyNullOrEmptyException.cs
@@ -0,0 +1,19 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class JsonPropertyNullOrEmptyException : Exception
+ {
+ public JsonPropertyNullOrEmptyException() : base() { }
+
+ public JsonPropertyNullOrEmptyException(string paramName) : base($"Value cannot be null or empty. (Parameter '{paramName}')") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/Nep11NotSupportedException.cs b/src/RestServer/Exceptions/Nep11NotSupportedException.cs
new file mode 100644
index 000000000..5382dbdef
--- /dev/null
+++ b/src/RestServer/Exceptions/Nep11NotSupportedException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class Nep11NotSupportedException : Exception
+ {
+ public Nep11NotSupportedException() { }
+ public Nep11NotSupportedException(UInt160 scriptHash) : base($"Contract '{scriptHash}' does not support NEP-11.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/Nep17NotSupportedException.cs b/src/RestServer/Exceptions/Nep17NotSupportedException.cs
new file mode 100644
index 000000000..00e672a1f
--- /dev/null
+++ b/src/RestServer/Exceptions/Nep17NotSupportedException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class Nep17NotSupportedException : Exception
+ {
+ public Nep17NotSupportedException() { }
+ public Nep17NotSupportedException(UInt160 scriptHash) : base($"Contract '{scriptHash}' does not support NEP-17.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/NodeException.cs b/src/RestServer/Exceptions/NodeException.cs
new file mode 100644
index 000000000..f59ec669d
--- /dev/null
+++ b/src/RestServer/Exceptions/NodeException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class NodeException : Exception
+ {
+ public NodeException() : base() { }
+ public NodeException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/NodeNetworkException.cs b/src/RestServer/Exceptions/NodeNetworkException.cs
new file mode 100644
index 000000000..da45244b2
--- /dev/null
+++ b/src/RestServer/Exceptions/NodeNetworkException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class NodeNetworkException : Exception
+ {
+ public NodeNetworkException() : base("Network does not match config file's.") { }
+ public NodeNetworkException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/QueryParameterNotFoundException.cs b/src/RestServer/Exceptions/QueryParameterNotFoundException.cs
new file mode 100644
index 000000000..8be1af0eb
--- /dev/null
+++ b/src/RestServer/Exceptions/QueryParameterNotFoundException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class QueryParameterNotFoundException : Exception
+ {
+ public QueryParameterNotFoundException() { }
+ public QueryParameterNotFoundException(string parameterName) : base($"Query parameter '{parameterName}' was not found.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/RestErrorCodes.cs b/src/RestServer/Exceptions/RestErrorCodes.cs
new file mode 100644
index 000000000..2fb434106
--- /dev/null
+++ b/src/RestServer/Exceptions/RestErrorCodes.cs
@@ -0,0 +1,19 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal static class RestErrorCodes
+ {
+ //=========================Rest Codes=========================
+ public const int GenericException = 1000;
+ public const int ParameterFormatException = 1001;
+ }
+}
diff --git a/src/RestServer/Exceptions/ScriptHashFormatException.cs b/src/RestServer/Exceptions/ScriptHashFormatException.cs
new file mode 100644
index 000000000..dcfe04f87
--- /dev/null
+++ b/src/RestServer/Exceptions/ScriptHashFormatException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class ScriptHashFormatException : Exception
+ {
+ public ScriptHashFormatException() : base() { }
+ public ScriptHashFormatException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/TransactionNotFoundException.cs b/src/RestServer/Exceptions/TransactionNotFoundException.cs
new file mode 100644
index 000000000..e27e810cc
--- /dev/null
+++ b/src/RestServer/Exceptions/TransactionNotFoundException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class TransactionNotFoundException : Exception
+ {
+ public TransactionNotFoundException() { }
+ public TransactionNotFoundException(UInt256 txhash) : base($"Transaction '{txhash}' was not found.") { }
+ }
+}
diff --git a/src/RestServer/Exceptions/UInt256FormatException.cs b/src/RestServer/Exceptions/UInt256FormatException.cs
new file mode 100644
index 000000000..9d77cf295
--- /dev/null
+++ b/src/RestServer/Exceptions/UInt256FormatException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class UInt256FormatException : Exception
+ {
+ public UInt256FormatException() { }
+ public UInt256FormatException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/WalletException.cs b/src/RestServer/Exceptions/WalletException.cs
new file mode 100644
index 000000000..47861b083
--- /dev/null
+++ b/src/RestServer/Exceptions/WalletException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class WalletException : Exception
+ {
+ public WalletException() : base() { }
+ public WalletException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/WalletInsufficientFundsException.cs b/src/RestServer/Exceptions/WalletInsufficientFundsException.cs
new file mode 100644
index 000000000..9b061c590
--- /dev/null
+++ b/src/RestServer/Exceptions/WalletInsufficientFundsException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class WalletInsufficientFundsException : Exception
+ {
+ public WalletInsufficientFundsException() : base("Wallet has insufficient funds.") { }
+ public WalletInsufficientFundsException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/WalletOpenException.cs b/src/RestServer/Exceptions/WalletOpenException.cs
new file mode 100644
index 000000000..bcdfa3317
--- /dev/null
+++ b/src/RestServer/Exceptions/WalletOpenException.cs
@@ -0,0 +1,19 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class WalletOpenException : Exception
+ {
+ public WalletOpenException() : base() { }
+
+ public WalletOpenException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Exceptions/WalletSessionException.cs b/src/RestServer/Exceptions/WalletSessionException.cs
new file mode 100644
index 000000000..b6db9bea7
--- /dev/null
+++ b/src/RestServer/Exceptions/WalletSessionException.cs
@@ -0,0 +1,18 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions
+{
+ internal class WalletSessionException : Exception
+ {
+ public WalletSessionException() : base() { }
+ public WalletSessionException(string message) : base(message) { }
+ }
+}
diff --git a/src/RestServer/Extensions/LedgerContractExtensions.cs b/src/RestServer/Extensions/LedgerContractExtensions.cs
new file mode 100644
index 000000000..7a86db8f8
--- /dev/null
+++ b/src/RestServer/Extensions/LedgerContractExtensions.cs
@@ -0,0 +1,142 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.IO;
+using Neo.Persistence;
+using Neo.Plugins.RestServer.Models.Blockchain;
+using Neo.SmartContract;
+using Neo.SmartContract.Native;
+using Neo.Wallets;
+
+#pragma warning disable IDE0060
+
+namespace Neo.Plugins.RestServer.Extensions
+{
+ internal static class LedgerContractExtensions
+ {
+ private const byte _prefix_block = 5;
+ private const byte _prefix_transaction = 11;
+ private const byte _prefix_account = 20;
+ private const byte _prefix_totalsupply = 11;
+
+ public static IEnumerable<(StorageKey key, StorageItem value)> GetStorageByPrefix(this ContractState contractState, DataCache snapshot, byte[] prefix)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(contractState));
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ if (prefix?.Length == 0)
+ throw new ArgumentNullException(nameof(prefix));
+ foreach (var (key, value) in snapshot.Find(StorageKey.CreateSearchPrefix(contractState.Id, prefix)))
+ yield return (key, value);
+ }
+
+ public static StorageItem GetStorageByKey(this ContractState contractState, DataCache snapshot, byte[] storageKey)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(contractState));
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ if (storageKey?.Length == 0)
+ throw new ArgumentNullException(nameof(storageKey));
+ foreach (var (key, value) in snapshot.Find(StorageKey.CreateSearchPrefix(contractState.Id, storageKey)))
+ if (key.Key.Span.SequenceEqual(storageKey))
+ return value;
+ return default;
+ }
+
+ public static IEnumerable<(StorageKey key, StorageItem value)> GetStorage(this ContractState contractState, DataCache snapshot)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(contractState));
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ return ListContractStorage(null, snapshot, contractState.Id);
+ }
+
+ public static IEnumerable<(StorageKey key, StorageItem value)> ListContractStorage(this ContractManagement contractManagement, DataCache snapshot, int contractId)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ if (contractId < 0)
+ throw new ArgumentOutOfRangeException(nameof(contractId));
+ foreach (var (key, value) in snapshot.Find(StorageKey.CreateSearchPrefix(contractId, ReadOnlySpan.Empty)))
+ yield return (key, value);
+ }
+
+ public static IEnumerable ListBlocks(this LedgerContract ledger, DataCache snapshot)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ var kb = new KeyBuilder(NativeContract.Ledger.Id, _prefix_block);
+ var prefixKey = kb.ToArray();
+ foreach (var (key, value) in snapshot.Seek(prefixKey, SeekDirection.Forward))
+ if (key.ToArray().AsSpan().StartsWith(prefixKey))
+ yield return value.Value.AsSerializable();
+ else
+ yield break;
+ }
+
+ public static IEnumerable ListTransactions(this LedgerContract ledger, DataCache snapshot, uint page, uint pageSize)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ var kb = new KeyBuilder(NativeContract.Ledger.Id, _prefix_transaction);
+ var prefixKey = kb.ToArray();
+ uint index = 1;
+ foreach (var (key, value) in snapshot.Seek(prefixKey, SeekDirection.Forward))
+ {
+ if (key.ToArray().AsSpan().StartsWith(prefixKey))
+ {
+ if (index >= page && index < (pageSize + page))
+ yield return value.GetInteroperable();
+ index++;
+ }
+ else
+ yield break;
+ }
+ }
+
+ public static IEnumerable ListAccounts(this GasToken gasToken, DataCache snapshot, ProtocolSettings protocolSettings)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ var kb = new KeyBuilder(gasToken.Id, _prefix_account);
+ var prefixKey = kb.ToArray();
+ foreach (var (key, value) in snapshot.Seek(prefixKey, SeekDirection.Forward))
+ {
+ if (key.ToArray().AsSpan().StartsWith(prefixKey))
+ {
+ var addressHash = new UInt160(key.ToArray().AsSpan(5));
+ yield return new AccountDetails()
+ {
+ ScriptHash = addressHash,
+ Address = addressHash.ToAddress(protocolSettings.AddressVersion),
+ Balance = value.GetInteroperable().Balance,
+ };
+ }
+ else
+ yield break;
+ }
+ }
+
+ public static IEnumerable ListAccounts(this NeoToken neoToken, DataCache snapshot, ProtocolSettings protocolSettings)
+ {
+ ArgumentNullException.ThrowIfNull(nameof(snapshot));
+ var kb = new KeyBuilder(neoToken.Id, _prefix_account);
+ var prefixKey = kb.ToArray();
+ foreach (var (key, value) in snapshot.Seek(prefixKey, SeekDirection.Forward))
+ {
+ if (key.ToArray().AsSpan().StartsWith(prefixKey))
+ {
+ var addressHash = new UInt160(key.ToArray().AsSpan(5));
+ yield return new AccountDetails()
+ {
+ ScriptHash = addressHash,
+ Address = addressHash.ToAddress(protocolSettings.AddressVersion),
+ Balance = value.GetInteroperable().Balance,
+ };
+ }
+ else
+ yield break;
+ }
+ }
+ }
+}
diff --git a/src/RestServer/Extensions/ModelExtensions.cs b/src/RestServer/Extensions/ModelExtensions.cs
new file mode 100644
index 000000000..027b559c5
--- /dev/null
+++ b/src/RestServer/Extensions/ModelExtensions.cs
@@ -0,0 +1,99 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P;
+using Neo.Plugins.RestServer.Models;
+using Neo.Plugins.RestServer.Models.Error;
+using Neo.Plugins.RestServer.Models.Node;
+using Neo.Plugins.RestServer.Models.Token;
+using Neo.Plugins.RestServer.Tokens;
+using Neo.SmartContract;
+
+namespace Neo.Plugins.RestServer.Extensions
+{
+ internal static class ModelExtensions
+ {
+ public static ExecutionEngineModel ToModel(this ApplicationEngine ae) =>
+ new()
+ {
+ GasConsumed = ae.GasConsumed,
+ State = ae.State,
+ Notifications = ae.Notifications.Select(s =>
+ new BlockchainEventModel()
+ {
+ ScriptHash = s.ScriptHash,
+ EventName = s.EventName,
+ State = s.State.ToArray(),
+ }).ToArray(),
+ ResultStack = ae.ResultStack.ToArray(),
+ FaultException = ae.FaultException == null ?
+ null :
+ new ErrorModel()
+ {
+ Code = ae.FaultException?.InnerException?.HResult ?? ae.FaultException.HResult,
+ Name = ae.FaultException?.InnerException?.GetType().Name ?? ae.FaultException?.GetType().Name,
+ Message = ae.FaultException?.InnerException?.Message ?? ae.FaultException?.Message,
+ },
+ };
+
+ public static NEP17TokenModel ToModel(this NEP17Token token) =>
+ new()
+ {
+ Name = token.Name,
+ Symbol = token.Symbol,
+ ScriptHash = token.ScriptHash,
+ Decimals = token.Decimals,
+ TotalSupply = token.TotalSupply().Value,
+ };
+
+ public static NEP11TokenModel ToModel(this NEP11Token nep11) =>
+ new()
+ {
+ Name = nep11.Name,
+ ScriptHash = nep11.ScriptHash,
+ Symbol = nep11.Symbol,
+ Decimals = nep11.Decimals,
+ TotalSupply = nep11.TotalSupply().Value,
+ Tokens = nep11.Tokens().Select(s => new
+ {
+ Key = s,
+ Value = nep11.Properties(s),
+ }).ToDictionary(key => Convert.ToHexString(key.Key), value => value.Value),
+ };
+
+ public static ProtocolSettingsModel ToModel(this ProtocolSettings protocolSettings) =>
+ new()
+ {
+ Network = protocolSettings.Network,
+ AddressVersion = protocolSettings.AddressVersion,
+ ValidatorsCount = protocolSettings.ValidatorsCount,
+ MillisecondsPerBlock = protocolSettings.MillisecondsPerBlock,
+ MaxValidUntilBlockIncrement = protocolSettings.MaxValidUntilBlockIncrement,
+ MaxTransactionsPerBlock = protocolSettings.MaxTransactionsPerBlock,
+ MemoryPoolMaxTransactions = protocolSettings.MemoryPoolMaxTransactions,
+ MaxTraceableBlocks = protocolSettings.MaxTraceableBlocks,
+ InitialGasDistribution = protocolSettings.InitialGasDistribution,
+ SeedList = protocolSettings.SeedList,
+ NativeUpdateHistory = protocolSettings.NativeUpdateHistory,
+ Hardforks = protocolSettings.Hardforks,
+ StandbyValidators = protocolSettings.StandbyValidators,
+ StandbyCommittee = protocolSettings.StandbyCommittee,
+ };
+
+ public static RemoteNodeModel ToModel(this RemoteNode remoteNode) =>
+ new()
+ {
+ RemoteAddress = remoteNode.Remote.Address.ToString(),
+ RemotePort = remoteNode.Remote.Port,
+ ListenTcpPort = remoteNode.ListenerTcpPort,
+ LastBlockIndex = remoteNode.LastBlockIndex,
+ };
+ }
+}
diff --git a/src/RestServer/Extensions/UInt160Extensions.cs b/src/RestServer/Extensions/UInt160Extensions.cs
new file mode 100644
index 000000000..472948257
--- /dev/null
+++ b/src/RestServer/Extensions/UInt160Extensions.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Helpers;
+using Neo.SmartContract.Native;
+
+namespace Neo.Plugins.RestServer.Extensions
+{
+ internal static class UInt160Extensions
+ {
+ public static bool IsValidNep17(this UInt160 scriptHash)
+ {
+ var contractState = NativeContract.ContractManagement.GetContract(RestServerPlugin.NeoSystem.StoreView, scriptHash);
+ return ContractHelper.IsNep17Supported(contractState);
+ }
+
+ public static bool IsValidContract(this UInt160 scriptHash) =>
+ NativeContract.ContractManagement.GetContract(RestServerPlugin.NeoSystem.StoreView, scriptHash) != null;
+ }
+}
diff --git a/src/RestServer/Helpers/ContractHelper.cs b/src/RestServer/Helpers/ContractHelper.cs
new file mode 100644
index 000000000..f2f7bcda0
--- /dev/null
+++ b/src/RestServer/Helpers/ContractHelper.cs
@@ -0,0 +1,58 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Persistence;
+using Neo.SmartContract;
+using Neo.SmartContract.Manifest;
+using Neo.SmartContract.Native;
+
+namespace Neo.Plugins.RestServer.Helpers
+{
+ public static class ContractHelper
+ {
+ public static ContractParameterDefinition[] GetAbiEventParams(DataCache snapshot, UInt160 scriptHash, string eventName)
+ {
+ var contractState = NativeContract.ContractManagement.GetContract(snapshot, scriptHash);
+ if (contractState == null)
+ return Array.Empty();
+ return contractState.Manifest.Abi.Events.SingleOrDefault(s => s.Name.Equals(eventName, StringComparison.OrdinalIgnoreCase))?.Parameters;
+ }
+
+ public static bool IsNep17Supported(DataCache snapshot, UInt160 scriptHash)
+ {
+ var contractState = NativeContract.ContractManagement.GetContract(snapshot, scriptHash);
+ if (contractState == null)
+ return false;
+ return IsNep17Supported(contractState);
+ }
+
+ public static bool IsNep11Supported(DataCache snapshot, UInt160 scriptHash)
+ {
+ var contractState = NativeContract.ContractManagement.GetContract(snapshot, scriptHash);
+ if (contractState == null)
+ return false;
+ return IsNep11Supported(contractState);
+ }
+
+ public static bool IsNep17Supported(ContractState contractState) =>
+ contractState.Manifest.SupportedStandards.Any(a => a.Equals("NEP-17"));
+
+ public static bool IsNep11Supported(ContractState contractState) =>
+ contractState.Manifest.SupportedStandards.Any(a => a.Equals("NEP-11"));
+
+ public static ContractMethodDescriptor GetContractMethod(DataCache snapshot, UInt160 scriptHash, string method, int pCount)
+ {
+ var contractState = NativeContract.ContractManagement.GetContract(snapshot, scriptHash);
+ if (contractState == null)
+ return null;
+ return contractState.Manifest.Abi.GetMethod(method, pCount);
+ }
+ }
+}
diff --git a/src/RestServer/Helpers/ScriptHelper.cs b/src/RestServer/Helpers/ScriptHelper.cs
new file mode 100644
index 000000000..092fdcc84
--- /dev/null
+++ b/src/RestServer/Helpers/ScriptHelper.cs
@@ -0,0 +1,59 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Neo.Persistence;
+using Neo.SmartContract;
+using Neo.SmartContract.Native;
+using Neo.VM;
+using Neo.VM.Types;
+using Array = System.Array;
+
+namespace Neo.Plugins.RestServer.Helpers
+{
+ internal static class ScriptHelper
+ {
+ public static bool InvokeMethod(ProtocolSettings protocolSettings, DataCache snapshot, UInt160 scriptHash, string method, out StackItem[] results, params object[] args)
+ {
+ using var scriptBuilder = new ScriptBuilder();
+ scriptBuilder.EmitDynamicCall(scriptHash, method, CallFlags.ReadOnly, args);
+ byte[] script = scriptBuilder.ToArray();
+ using var engine = ApplicationEngine.Run(script, snapshot, settings: protocolSettings, gas: RestServerSettings.Current.MaxGasInvoke);
+ results = engine.State == VMState.FAULT ? Array.Empty() : engine.ResultStack.ToArray();
+ return engine.State == VMState.HALT;
+ }
+
+ public static ApplicationEngine InvokeMethod(ProtocolSettings protocolSettings, DataCache snapshot, UInt160 scriptHash, string method, ContractParameter[] args, out byte[] script)
+ {
+ using var scriptBuilder = new ScriptBuilder();
+ scriptBuilder.EmitDynamicCall(scriptHash, method, CallFlags.ReadOnly, args);
+ script = scriptBuilder.ToArray();
+ using var engine = ApplicationEngine.Run(script, snapshot, settings: protocolSettings, gas: RestServerSettings.Current.MaxGasInvoke);
+ return engine;
+ }
+
+ public static ApplicationEngine InvokeScript(ReadOnlyMemory script, Signer[] signers = null, Witness[] witnesses = null)
+ {
+ var neosystem = RestServerPlugin.NeoSystem;
+ var snapshot = neosystem.GetSnapshot();
+ Transaction tx = signers == null ? null : new Transaction
+ {
+ Version = 0,
+ Nonce = (uint)Random.Shared.Next(),
+ ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + neosystem.Settings.MaxValidUntilBlockIncrement,
+ Signers = signers,
+ Attributes = Array.Empty(),
+ Script = script,
+ Witnesses = witnesses
+ };
+ return ApplicationEngine.Run(script, snapshot, tx, settings: neosystem.Settings, gas: RestServerSettings.Current.MaxGasInvoke);
+ }
+ }
+}
diff --git a/src/RestServer/Middleware/RestServerMiddleware.cs b/src/RestServer/Middleware/RestServerMiddleware.cs
new file mode 100644
index 000000000..fe9a18c9d
--- /dev/null
+++ b/src/RestServer/Middleware/RestServerMiddleware.cs
@@ -0,0 +1,43 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.AspNetCore.Http;
+using System.Reflection;
+
+namespace Neo.Plugins.RestServer.Middleware
+{
+ internal class RestServerMiddleware
+ {
+ private readonly RequestDelegate _next;
+
+ public RestServerMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ var request = context.Request;
+ var response = context.Response;
+
+ SetServerInfomationHeader(response);
+
+ await _next(context);
+ }
+
+ public static void SetServerInfomationHeader(HttpResponse response)
+ {
+ var neoCliAsm = Assembly.GetEntryAssembly().GetName();
+ var restServerAsm = Assembly.GetExecutingAssembly().GetName();
+
+ response.Headers.Server = $"{neoCliAsm.Name}/{neoCliAsm.Version.ToString(3)} {restServerAsm.Name}/{restServerAsm.Version.ToString(3)}";
+ }
+ }
+}
diff --git a/src/RestServer/Models/Blockchain/AccountDetails.cs b/src/RestServer/Models/Blockchain/AccountDetails.cs
new file mode 100644
index 000000000..5acb0ceec
--- /dev/null
+++ b/src/RestServer/Models/Blockchain/AccountDetails.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Blockchain
+{
+ internal class AccountDetails
+ {
+ ///
+ /// Scripthash
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Wallet address.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Balance of the account.
+ ///
+ /// 10000000
+ public BigInteger Balance { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/CountModel.cs b/src/RestServer/Models/CountModel.cs
new file mode 100644
index 000000000..afbf09145
--- /dev/null
+++ b/src/RestServer/Models/CountModel.cs
@@ -0,0 +1,21 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models
+{
+ internal class CountModel
+ {
+ ///
+ /// The count of how many objects.
+ ///
+ /// 378
+ public int Count { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Error/ErrorModel.cs b/src/RestServer/Models/Error/ErrorModel.cs
new file mode 100644
index 000000000..bc35543fa
--- /dev/null
+++ b/src/RestServer/Models/Error/ErrorModel.cs
@@ -0,0 +1,32 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Error
+{
+ internal class ErrorModel
+ {
+ ///
+ /// Error's HResult Code.
+ ///
+ /// 1000
+ public int Code { get; init; } = 1000;
+ ///
+ /// Error's name of the type.
+ ///
+ /// GeneralException
+ public string Name { get; init; } = "GeneralException";
+ ///
+ /// Error's exception message.
+ ///
+ /// An error occurred.
+ /// Could be InnerException message as well, If exists.
+ public string Message { get; init; } = "An error occurred.";
+ }
+}
diff --git a/src/RestServer/Models/Error/ParameterFormatExceptionModel.cs b/src/RestServer/Models/Error/ParameterFormatExceptionModel.cs
new file mode 100644
index 000000000..b842123da
--- /dev/null
+++ b/src/RestServer/Models/Error/ParameterFormatExceptionModel.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions;
+
+namespace Neo.Plugins.RestServer.Models.Error
+{
+ internal class ParameterFormatExceptionModel : ErrorModel
+ {
+ public ParameterFormatExceptionModel()
+ {
+ Code = RestErrorCodes.ParameterFormatException;
+ Name = nameof(RestErrorCodes.ParameterFormatException);
+ }
+
+ public ParameterFormatExceptionModel(string message) : this()
+ {
+ Message = message;
+ }
+ }
+}
diff --git a/src/RestServer/Models/ExecutionEngineModel.cs b/src/RestServer/Models/ExecutionEngineModel.cs
new file mode 100644
index 000000000..5710e550f
--- /dev/null
+++ b/src/RestServer/Models/ExecutionEngineModel.cs
@@ -0,0 +1,49 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Error;
+using Neo.SmartContract;
+using Neo.VM;
+using Neo.VM.Types;
+
+namespace Neo.Plugins.RestServer.Models
+{
+ internal class ExecutionEngineModel
+ {
+ public long GasConsumed { get; set; } = 0L;
+ public VMState State { get; set; } = VMState.NONE;
+ public BlockchainEventModel[] Notifications { get; set; } = System.Array.Empty();
+ public StackItem[] ResultStack { get; set; } = System.Array.Empty();
+ public ErrorModel FaultException { get; set; }
+ }
+
+ internal class BlockchainEventModel
+ {
+ public UInt160 ScriptHash { get; set; } = new();
+ public string EventName { get; set; } = string.Empty;
+ public StackItem[] State { get; set; } = System.Array.Empty();
+
+ public static BlockchainEventModel Create(UInt160 scriptHash, string eventName, StackItem[] state) =>
+ new()
+ {
+ ScriptHash = scriptHash,
+ EventName = eventName ?? string.Empty,
+ State = state,
+ };
+
+ public static BlockchainEventModel Create(NotifyEventArgs notifyEventArgs, StackItem[] state) =>
+ new()
+ {
+ ScriptHash = notifyEventArgs.ScriptHash,
+ EventName = notifyEventArgs.EventName,
+ State = state,
+ };
+ }
+}
diff --git a/src/RestServer/Models/Ledger/MemoryPoolCountModel.cs b/src/RestServer/Models/Ledger/MemoryPoolCountModel.cs
new file mode 100644
index 000000000..d411f0eac
--- /dev/null
+++ b/src/RestServer/Models/Ledger/MemoryPoolCountModel.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Ledger
+{
+ internal class MemoryPoolCountModel
+ {
+ ///
+ /// Total count all transactions.
+ ///
+ /// 110
+ public int Count { get; set; }
+ ///
+ /// Count of unverified transactions
+ ///
+ /// 10
+ public int UnVerifiedCount { get; set; }
+ ///
+ /// Count of verified transactions.
+ ///
+ /// 100
+ public int VerifiedCount { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Node/PluginModel.cs b/src/RestServer/Models/Node/PluginModel.cs
new file mode 100644
index 000000000..36d388d17
--- /dev/null
+++ b/src/RestServer/Models/Node/PluginModel.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Node
+{
+ internal class PluginModel
+ {
+ ///
+ /// Name
+ ///
+ /// RestServer
+ public string Name { get; set; }
+ ///
+ /// Version
+ ///
+ /// 3.5.0
+ public string Version { get; set; }
+ ///
+ /// Description
+ ///
+ /// Enables REST Web Sevices for the node
+ public string Description { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Node/ProtocolSettingsModel.cs b/src/RestServer/Models/Node/ProtocolSettingsModel.cs
new file mode 100644
index 000000000..590ad7f04
--- /dev/null
+++ b/src/RestServer/Models/Node/ProtocolSettingsModel.cs
@@ -0,0 +1,40 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+
+namespace Neo.Plugins.RestServer.Models.Node
+{
+ internal class ProtocolSettingsModel
+ {
+ ///
+ /// Network
+ ///
+ /// 860833102
+ public uint Network { get; set; }
+ ///
+ /// AddressVersion
+ ///
+ /// 53
+ public byte AddressVersion { get; set; }
+ public int ValidatorsCount { get; set; }
+ public uint MillisecondsPerBlock { get; set; }
+ public uint MaxValidUntilBlockIncrement { get; set; }
+ public uint MaxTransactionsPerBlock { get; set; }
+ public int MemoryPoolMaxTransactions { get; set; }
+ public uint MaxTraceableBlocks { get; set; }
+ public ulong InitialGasDistribution { get; set; }
+ public IReadOnlyCollection SeedList { get; set; }
+ public IReadOnlyDictionary NativeUpdateHistory { get; set; }
+ public IReadOnlyDictionary Hardforks { get; set; }
+ public IReadOnlyList StandbyValidators { get; set; }
+ public IReadOnlyList StandbyCommittee { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Node/RemoteNodeModel.cs b/src/RestServer/Models/Node/RemoteNodeModel.cs
new file mode 100644
index 000000000..647ec5e79
--- /dev/null
+++ b/src/RestServer/Models/Node/RemoteNodeModel.cs
@@ -0,0 +1,36 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Node
+{
+ public class RemoteNodeModel
+ {
+ ///
+ /// Remote peer's ip address.
+ ///
+ /// 10.0.0.100
+ public string RemoteAddress { get; set; }
+ ///
+ /// Remote peer's port number.
+ ///
+ /// 20333
+ public int RemotePort { get; set; }
+ ///
+ /// Remote peer's listening tcp port.
+ ///
+ /// 20333
+ public int ListenTcpPort { get; set; }
+ ///
+ /// Remote peer's last synced block height.
+ ///
+ /// 2584158
+ public uint LastBlockIndex { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Token/NEP11TokenModel.cs b/src/RestServer/Models/Token/NEP11TokenModel.cs
new file mode 100644
index 000000000..a454646ea
--- /dev/null
+++ b/src/RestServer/Models/Token/NEP11TokenModel.cs
@@ -0,0 +1,19 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.VM.Types;
+
+namespace Neo.Plugins.RestServer.Models.Token
+{
+ internal class NEP11TokenModel : NEP17TokenModel
+ {
+ public IReadOnlyDictionary> Tokens { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Token/NEP17TokenModel.cs b/src/RestServer/Models/Token/NEP17TokenModel.cs
new file mode 100644
index 000000000..b769a3eed
--- /dev/null
+++ b/src/RestServer/Models/Token/NEP17TokenModel.cs
@@ -0,0 +1,23 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Token
+{
+ internal class NEP17TokenModel
+ {
+ public string Name { get; set; }
+ public UInt160 ScriptHash { get; set; }
+ public string Symbol { get; set; }
+ public byte Decimals { get; set; }
+ public BigInteger TotalSupply { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Token/TokenBalanceModel.cs b/src/RestServer/Models/Token/TokenBalanceModel.cs
new file mode 100644
index 000000000..a3a10e7d0
--- /dev/null
+++ b/src/RestServer/Models/Token/TokenBalanceModel.cs
@@ -0,0 +1,24 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Token
+{
+ public class TokenBalanceModel
+ {
+ public string Name { get; set; }
+ public UInt160 ScriptHash { get; set; }
+ public string Symbol { get; set; }
+ public byte Decimals { get; set; }
+ public BigInteger Balance { get; set; }
+ public BigInteger TotalSupply { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Utils/UtilsAddressIsValidModel.cs b/src/RestServer/Models/Utils/UtilsAddressIsValidModel.cs
new file mode 100644
index 000000000..7295f9a13
--- /dev/null
+++ b/src/RestServer/Models/Utils/UtilsAddressIsValidModel.cs
@@ -0,0 +1,11 @@
+namespace Neo.Plugins.RestServer.Models.Utils
+{
+ internal class UtilsAddressIsValidModel : UtilsAddressModel
+ {
+ ///
+ /// Indicates if address can be converted to ScriptHash or Neo Address.
+ ///
+ /// true
+ public bool IsValid { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Utils/UtilsAddressModel.cs b/src/RestServer/Models/Utils/UtilsAddressModel.cs
new file mode 100644
index 000000000..9b9694159
--- /dev/null
+++ b/src/RestServer/Models/Utils/UtilsAddressModel.cs
@@ -0,0 +1,21 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Utils
+{
+ internal class UtilsAddressModel
+ {
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public virtual string Address { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Utils/UtilsScriptHashModel.cs b/src/RestServer/Models/Utils/UtilsScriptHashModel.cs
new file mode 100644
index 000000000..5a24603ba
--- /dev/null
+++ b/src/RestServer/Models/Utils/UtilsScriptHashModel.cs
@@ -0,0 +1,21 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Utils
+{
+ internal class UtilsScriptHashModel
+ {
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletAccountBalanceModel.cs b/src/RestServer/Models/Wallet/WalletAccountBalanceModel.cs
new file mode 100644
index 000000000..b920c8466
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletAccountBalanceModel.cs
@@ -0,0 +1,26 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Wallet account balance object.
+ ///
+ internal class WalletAccountBalanceModel
+ {
+ ///
+ /// Balance of the account.
+ ///
+ /// 10000000
+ public BigInteger Balance { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletAddressModel.cs b/src/RestServer/Models/Wallet/WalletAddressModel.cs
new file mode 100644
index 000000000..d2d0184bd
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletAddressModel.cs
@@ -0,0 +1,51 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Wallet address object.
+ ///
+ public class WalletAddressModel
+ {
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Public key of the wallet address.
+ ///
+ /// 03cdb067d930fd5adaa6c68545016044aaddec64ba39e548250eaea551172e535c
+ public ECPoint Publickey { get; set; }
+ ///
+ /// has a private key or not.
+ ///
+ /// true
+ public bool HasKey { get; set; }
+ ///
+ /// The display name for the address.
+ ///
+ /// Default Account
+ public string Label { get; set; }
+ ///
+ /// is the address a WatchOnly address.
+ ///
+ /// false
+ public bool WatchOnly { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletAssetModel.cs b/src/RestServer/Models/Wallet/WalletAssetModel.cs
new file mode 100644
index 000000000..9d0766e2a
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletAssetModel.cs
@@ -0,0 +1,54 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ public class WalletAssetModel
+ {
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Public key of the wallet address.
+ ///
+ /// 03cdb067d930fd5adaa6c68545016044aaddec64ba39e548250eaea551172e535c
+ public ECPoint PublicKey { get; set; }
+ ///
+ /// Neo amount.
+ ///
+ /// 1
+ public BigInteger Neo { get; set; }
+ ///
+ /// Neo ScriptHash Address.
+ ///
+ /// 0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5
+ public UInt160 NeoHash { get; set; }
+ ///
+ /// Gas amount.
+ ///
+ /// 10000000
+ public BigInteger Gas { get; set; }
+ ///
+ /// Gas ScriptHash Address.
+ ///
+ /// 0xd2a4cff31913016155e38e474a2c06d08be276cf
+ public UInt160 GasHash { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletChangePasswordModel.cs b/src/RestServer/Models/Wallet/WalletChangePasswordModel.cs
new file mode 100644
index 000000000..d0aa6b034
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletChangePasswordModel.cs
@@ -0,0 +1,40 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ public class WalletChangePasswordModel
+ {
+ ///
+ /// Current password.
+ ///
+ /// Password1!
+ [Required(AllowEmptyStrings = false)]
+ public string OldPassword { get; set; }
+ ///
+ /// New Password.
+ ///
+ /// HelloWorld1!
+ [Required(AllowEmptyStrings = false)]
+ public string NewPassword { get; set; }
+ ///
+ /// Should create a backup file.
+ ///
+ /// false
+ public bool CreateBackupFile { get; set; }
+ ///
+ /// if backup file exists overwrite it.
+ ///
+ /// false
+ public bool OverwriteIfBackupFileExists { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletCreateAccountModel.cs b/src/RestServer/Models/Wallet/WalletCreateAccountModel.cs
new file mode 100644
index 000000000..5e3f1f36b
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletCreateAccountModel.cs
@@ -0,0 +1,24 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Create account object.
+ ///
+ public class WalletCreateAccountModel
+ {
+ ///
+ /// Private key of the address you want to create. Can be null or empty.
+ ///
+ /// CHeABTw3Q5SkjWharPAhgE+p+rGVN9FhlO4hXoJZQqA=
+ public byte[] PrivateKey { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletCreateModel.cs b/src/RestServer/Models/Wallet/WalletCreateModel.cs
new file mode 100644
index 000000000..82c0cdebd
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletCreateModel.cs
@@ -0,0 +1,45 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Wallet create object.
+ ///
+ public class WalletCreateModel
+ {
+ ///
+ /// Account display name
+ ///
+ /// Default Account
+ /// Can be null.
+ public string Name { get; set; }
+ ///
+ /// Path of the wallet file relative to the neo-cli path.
+ ///
+ /// ./wallets/mywallet.json
+ [Required(AllowEmptyStrings = false)]
+ public string Path { get; set; }
+ ///
+ /// Representation of the private.
+ ///
+ /// L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU
+ /// Can be null or empty.
+ public string Wif { get; set; }
+ ///
+ /// Password to open the wallet file.
+ ///
+ /// Password1!
+ [Required(AllowEmptyStrings = false)]
+ public string Password { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletExportKeyModel.cs b/src/RestServer/Models/Wallet/WalletExportKeyModel.cs
new file mode 100644
index 000000000..8d18e88b7
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletExportKeyModel.cs
@@ -0,0 +1,34 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Export key wallet object
+ ///
+ internal class WalletExportKeyModel
+ {
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Representation of the private.
+ ///
+ /// L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU
+ public string Wif { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletImportKey.cs b/src/RestServer/Models/Wallet/WalletImportKey.cs
new file mode 100644
index 000000000..2dfe03c84
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletImportKey.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Wallet import key object.
+ ///
+ public class WalletImportKey
+ {
+ ///
+ /// Representation of the private.
+ ///
+ /// L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU
+ [Required(AllowEmptyStrings = false)]
+ public string Wif { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletImportMultiSigAddressModel.cs b/src/RestServer/Models/Wallet/WalletImportMultiSigAddressModel.cs
new file mode 100644
index 000000000..e22fe944c
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletImportMultiSigAddressModel.cs
@@ -0,0 +1,32 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Import Multi-Signature Address Object.
+ ///
+ public class WalletImportMultiSigAddressModel
+ {
+ ///
+ /// Minimum required signatures to sign.
+ ///
+ /// 2
+ public ushort RequiredSignatures { get; set; }
+ ///
+ /// Array of public keys of the addresses.
+ ///
+ [Required]
+ public ECPoint[] PublicKeys { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletKeyModel.cs b/src/RestServer/Models/Wallet/WalletKeyModel.cs
new file mode 100644
index 000000000..3d8f1f176
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletKeyModel.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ public class WalletKeyModel
+ {
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Public key of the wallet address.
+ ///
+ /// 03cdb067d930fd5adaa6c68545016044aaddec64ba39e548250eaea551172e535c
+ public ECPoint PublicKey { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletMultiSignContractModel.cs b/src/RestServer/Models/Wallet/WalletMultiSignContractModel.cs
new file mode 100644
index 000000000..d04606de0
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletMultiSignContractModel.cs
@@ -0,0 +1,34 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Multi-Signature Contract Object.
+ ///
+ internal class WalletMultiSignContractModel
+ {
+ ///
+ /// Wallet address that was exported.
+ ///
+ /// NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs
+ public string Address { get; set; }
+ ///
+ /// Scripthash of the wallet account exported.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ public UInt160 ScriptHash { get; set; }
+ ///
+ /// Script that used to create the address
+ ///
+ /// CHeABTw3Q5SkjWharPAhgE+p+rGVN9FhlO4hXoJZQqA=
+ public byte[] Script { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletOpenModel.cs b/src/RestServer/Models/Wallet/WalletOpenModel.cs
new file mode 100644
index 000000000..34ebe6bbc
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletOpenModel.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Open wallet request object.
+ ///
+ public class WalletOpenModel
+ {
+ ///
+ /// Path of the wallet file relative to the neo-cli path.
+ ///
+ /// ./wallets/mywallet.json
+ [Required(AllowEmptyStrings = false)]
+ public string Path { get; set; }
+ ///
+ /// Password to open the wallet file.
+ ///
+ /// Password1!
+ [Required(AllowEmptyStrings = false)]
+ public string Password { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletSendModel.cs b/src/RestServer/Models/Wallet/WalletSendModel.cs
new file mode 100644
index 000000000..d2c031520
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletSendModel.cs
@@ -0,0 +1,58 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+using System.Numerics;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Wallet send object.
+ ///
+ public class WalletSendModel
+ {
+ ///
+ /// Asset Id
+ ///
+ /// 0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5
+ [Required]
+ public UInt160 AssetId { get; set; }
+ ///
+ /// ScriptHash of the address in the wallet to send from.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ [Required]
+ public UInt160 From { get; set; }
+ ///
+ /// ScriptHash of the address in the wallet to send too.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ [Required]
+ public UInt160 To { get; set; }
+ ///
+ /// Amount
+ ///
+ /// 1
+ /// Not user representation.
+ [Required]
+ public BigInteger Amount { get; set; }
+ ///
+ /// Data you would like to send.
+ ///
+ /// can be null or empty.
+ /// SGVsbG8gV29ybGQ=
+ public byte[] Data { get; set; }
+ ///
+ /// An array of the signer that will be signing the transaction.
+ ///
+ /// Can be null
+ public UInt160[] Signers { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletSessionModel.cs b/src/RestServer/Models/Wallet/WalletSessionModel.cs
new file mode 100644
index 000000000..ef226f8d4
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletSessionModel.cs
@@ -0,0 +1,24 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Models.Wallet
+{
+ ///
+ /// Open/Created wallet session object.
+ ///
+ public class WalletSessionModel
+ {
+ ///
+ /// Session id for an open/created wallet.
+ ///
+ /// 066843daf5ce45aba803587780998cdb
+ public Guid SessionId { get; set; }
+ }
+}
diff --git a/src/RestServer/Models/Wallet/WalletTransactionScriptModel.cs b/src/RestServer/Models/Wallet/WalletTransactionScriptModel.cs
new file mode 100644
index 000000000..833563cb6
--- /dev/null
+++ b/src/RestServer/Models/Wallet/WalletTransactionScriptModel.cs
@@ -0,0 +1,35 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.ComponentModel.DataAnnotations;
+
+namespace Neo.Plugins.RestServer.Models.Wallet
+{
+ public class WalletTransactionScriptModel
+ {
+ ///
+ /// Script to use.
+ ///
+ /// CHeABTw3Q5SkjWharPAhgE+p+rGVN9FhlO4hXoJZQqA=
+ [Required]
+ public byte[] Script { get; set; }
+ ///
+ /// ScriptHash of the address in the wallet to send from.
+ ///
+ /// 0xed7cc6f5f2dd842d384f254bc0c2d58fb69a4761
+ [Required]
+ public UInt160 From { get; set; }
+ ///
+ /// An array of the signer that will be signing the transaction.
+ ///
+ /// Can be null
+ public UInt160[] Signers { get; set; }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/BigDecimalJsonConverter.cs b/src/RestServer/Newtonsoft/Json/BigDecimalJsonConverter.cs
new file mode 100644
index 000000000..20d8be78a
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/BigDecimalJsonConverter.cs
@@ -0,0 +1,48 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Numerics;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class BigDecimalJsonConverter : JsonConverter
+ {
+ public override bool CanRead => true;
+ public override bool CanWrite => true;
+
+ public override BigDecimal ReadJson(JsonReader reader, Type objectType, BigDecimal existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var token = JToken.ReadFrom(reader);
+ if (token.Type == JTokenType.Object)
+ {
+ var valueProp = ((JObject)token).Properties().SingleOrDefault(p => p.Name.Equals("value", StringComparison.InvariantCultureIgnoreCase));
+ var decimalsProp = ((JObject)token).Properties().SingleOrDefault(p => p.Name.Equals("decimals", StringComparison.InvariantCultureIgnoreCase));
+
+ if (valueProp != null && decimalsProp != null)
+ {
+ return new BigDecimal(valueProp.ToObject(), decimalsProp.ToObject());
+ }
+ }
+ throw new FormatException();
+ }
+
+ public override void WriteJson(JsonWriter writer, BigDecimal value, JsonSerializer serializer)
+ {
+ var o = JToken.FromObject(new
+ {
+ value.Value,
+ value.Decimals,
+ }, serializer);
+ o.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/BlockHeaderJsonConverter.cs b/src/RestServer/Newtonsoft/Json/BlockHeaderJsonConverter.cs
new file mode 100644
index 000000000..5d30b5efe
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/BlockHeaderJsonConverter.cs
@@ -0,0 +1,32 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class BlockHeaderJsonConverter : JsonConverter
+ {
+ public override bool CanRead => false;
+ public override bool CanWrite => true;
+
+ public override Header ReadJson(JsonReader reader, Type objectType, Header existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteJson(JsonWriter writer, Header value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.BlockHeaderToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/BlockJsonConverter.cs b/src/RestServer/Newtonsoft/Json/BlockJsonConverter.cs
new file mode 100644
index 000000000..113053af7
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/BlockJsonConverter.cs
@@ -0,0 +1,30 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class BlockJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override Block ReadJson(JsonReader reader, Type objectType, Block existingValue, bool hasExistingValue, JsonSerializer serializer) =>
+ throw new NotImplementedException();
+
+ public override void WriteJson(JsonWriter writer, Block value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.BlockToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractAbiJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractAbiJsonConverter.cs
new file mode 100644
index 000000000..47ba189df
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractAbiJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractAbiJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractAbi ReadJson(JsonReader reader, Type objectType, ContractAbi existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractAbi value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractAbiToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractEventDescriptorJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractEventDescriptorJsonConverter.cs
new file mode 100644
index 000000000..510664275
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractEventDescriptorJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractEventDescriptorJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractEventDescriptor ReadJson(JsonReader reader, Type objectType, ContractEventDescriptor existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractEventDescriptor value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractEventToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractGroupJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractGroupJsonConverter.cs
new file mode 100644
index 000000000..0e58fa010
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractGroupJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractGroupJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractGroup ReadJson(JsonReader reader, Type objectType, ContractGroup existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractGroup value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractGroupToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractJsonConverter.cs
new file mode 100644
index 000000000..0a55e26d7
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractState ReadJson(JsonReader reader, Type objectType, ContractState existingValue, bool hasExistingValue, global::Newtonsoft.Json.JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractState value, global::Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractStateToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractManifestJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractManifestJsonConverter.cs
new file mode 100644
index 000000000..ae602e755
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractManifestJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractManifestJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractManifest ReadJson(JsonReader reader, Type objectType, ContractManifest existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractManifest value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractManifestToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractMethodJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractMethodJsonConverter.cs
new file mode 100644
index 000000000..2d7155b0d
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractMethodJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractMethodJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractMethodDescriptor ReadJson(JsonReader reader, Type objectType, ContractMethodDescriptor existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractMethodDescriptor value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractMethodToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractMethodParametersJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractMethodParametersJsonConverter.cs
new file mode 100644
index 000000000..3b80a30c0
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractMethodParametersJsonConverter.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+public class ContractMethodParametersJsonConverter : JsonConverter
+{
+ public override bool CanRead => base.CanRead;
+
+ public override bool CanWrite => base.CanWrite;
+
+ public override ContractParameterDefinition ReadJson(JsonReader reader, Type objectType, ContractParameterDefinition existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractParameterDefinition value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractMethodParameterToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractParameterDefinitionJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractParameterDefinitionJsonConverter.cs
new file mode 100644
index 000000000..5a99c9812
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractParameterDefinitionJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractParameterDefinitionJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractParameterDefinition ReadJson(JsonReader reader, Type objectType, ContractParameterDefinition existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractParameterDefinition value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractParameterDefinitionToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractParameterJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractParameterJsonConverter.cs
new file mode 100644
index 000000000..d32b45242
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractParameterJsonConverter.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class ContractParameterJsonConverter : JsonConverter
+ {
+ public override bool CanRead => true;
+ public override bool CanWrite => false;
+
+ public override ContractParameter ReadJson(JsonReader reader, Type objectType, ContractParameter existingValue, bool hasExistingValue, global::Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var token = JToken.ReadFrom(reader);
+ return RestServerUtility.ContractParameterFromJToken(token);
+ }
+
+ public override void WriteJson(JsonWriter writer, ContractParameter value, global::Newtonsoft.Json.JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractPermissionDescriptorJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractPermissionDescriptorJsonConverter.cs
new file mode 100644
index 000000000..8c85ee764
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractPermissionDescriptorJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class ContractPermissionDescriptorJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractPermissionDescriptor ReadJson(JsonReader reader, Type objectType, ContractPermissionDescriptor existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractPermissionDescriptor value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractPermissionDescriptorToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ContractPermissionJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ContractPermissionJsonConverter.cs
new file mode 100644
index 000000000..c4747f065
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ContractPermissionJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+internal class ContractPermissionJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override ContractPermission ReadJson(JsonReader reader, Type objectType, ContractPermission existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, ContractPermission value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractPermissionToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ECPointJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ECPointJsonConverter.cs
new file mode 100644
index 000000000..99b514b13
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ECPointJsonConverter.cs
@@ -0,0 +1,37 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Neo.Plugins.RestServer.Exceptions;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class ECPointJsonConverter : JsonConverter
+ {
+ public override ECPoint ReadJson(JsonReader reader, Type objectType, ECPoint existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var value = reader?.Value.ToString();
+ try
+ {
+ return ECPoint.Parse(value, ECCurve.Secp256r1);
+ }
+ catch (FormatException)
+ {
+ throw new UInt256FormatException($"{value} is invalid.");
+ }
+ }
+
+ public override void WriteJson(JsonWriter writer, ECPoint value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString());
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/GuidJsonConverter.cs b/src/RestServer/Newtonsoft/Json/GuidJsonConverter.cs
new file mode 100644
index 000000000..024a00f77
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/GuidJsonConverter.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ internal class GuidJsonConverter : JsonConverter
+ {
+ public override Guid ReadJson(JsonReader reader, Type objectType, Guid existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ return Guid.Parse(reader.Value?.ToString());
+ }
+
+ public override void WriteJson(JsonWriter writer, Guid value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString("n"));
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/InteropInterfaceJsonConverter.cs b/src/RestServer/Newtonsoft/Json/InteropInterfaceJsonConverter.cs
new file mode 100644
index 000000000..8727577fc
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/InteropInterfaceJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.VM.Types;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class InteropInterfaceJsonConverter : JsonConverter
+ {
+ public override InteropInterface ReadJson(JsonReader reader, Type objectType, InteropInterface existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.Load(reader);
+ return RestServerUtility.StackItemFromJToken(t) as InteropInterface;
+ }
+
+ public override void WriteJson(JsonWriter writer, InteropInterface value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/MethodTokenJsonConverter.cs b/src/RestServer/Newtonsoft/Json/MethodTokenJsonConverter.cs
new file mode 100644
index 000000000..7503d3a67
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/MethodTokenJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class MethodTokenJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override MethodToken ReadJson(JsonReader reader, Type objectType, MethodToken existingValue, bool hasExistingValue, global::Newtonsoft.Json.JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, MethodToken value, global::Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var j = RestServerUtility.MethodTokenToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/NefFileJsonConverter.cs b/src/RestServer/Newtonsoft/Json/NefFileJsonConverter.cs
new file mode 100644
index 000000000..d4e254d7e
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/NefFileJsonConverter.cs
@@ -0,0 +1,24 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class NefFileJsonConverter : JsonConverter
+{
+ public override NefFile ReadJson(JsonReader reader, Type objectType, NefFile existingValue, bool hasExistingValue, global::Newtonsoft.Json.JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, NefFile value, global::Newtonsoft.Json.JsonSerializer serializer)
+ {
+ var j = RestServerUtility.ContractNefFileToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/ReadOnlyMemoryBytesJsonConverter.cs b/src/RestServer/Newtonsoft/Json/ReadOnlyMemoryBytesJsonConverter.cs
new file mode 100644
index 000000000..99ea3d132
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/ReadOnlyMemoryBytesJsonConverter.cs
@@ -0,0 +1,29 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class ReadOnlyMemoryBytesJsonConverter : JsonConverter>
+ {
+ public override ReadOnlyMemory ReadJson(JsonReader reader, Type objectType, ReadOnlyMemory existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var o = JToken.Load(reader);
+ return Convert.FromBase64String(o.ToObject());
+ }
+
+ public override void WriteJson(JsonWriter writer, ReadOnlyMemory value, JsonSerializer serializer)
+ {
+ writer.WriteValue(Convert.ToBase64String(value.Span));
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/SignerJsonConverter.cs b/src/RestServer/Newtonsoft/Json/SignerJsonConverter.cs
new file mode 100644
index 000000000..50d596010
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/SignerJsonConverter.cs
@@ -0,0 +1,29 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class SignerJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override Signer ReadJson(JsonReader reader, Type objectType, Signer existingValue, bool hasExistingValue, JsonSerializer serializer) =>
+ throw new NotImplementedException();
+
+ public override void WriteJson(JsonWriter writer, Signer value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.SignerToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/StackItemJsonConverter.cs b/src/RestServer/Newtonsoft/Json/StackItemJsonConverter.cs
new file mode 100644
index 000000000..dae151cb8
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/StackItemJsonConverter.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.VM.Types;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class StackItemJsonConverter : JsonConverter
+ {
+ public override StackItem ReadJson(JsonReader reader, Type objectType, StackItem existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JObject.Load(reader);
+ return RestServerUtility.StackItemFromJToken(t);
+ }
+
+ public override void WriteJson(JsonWriter writer, StackItem value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+
+
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/TransactionAttributeJsonConverter.cs b/src/RestServer/Newtonsoft/Json/TransactionAttributeJsonConverter.cs
new file mode 100644
index 000000000..4bc950fc9
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/TransactionAttributeJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class TransactionAttributeJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override TransactionAttribute ReadJson(JsonReader reader, Type objectType, TransactionAttribute existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, TransactionAttribute value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.TransactionAttributeToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/TransactionJsonConverter.cs b/src/RestServer/Newtonsoft/Json/TransactionJsonConverter.cs
new file mode 100644
index 000000000..18080fe6a
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/TransactionJsonConverter.cs
@@ -0,0 +1,28 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Network.P2P.Payloads;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json;
+
+public class TransactionJsonConverter : JsonConverter
+{
+ public override bool CanRead => false;
+
+ public override bool CanWrite => true;
+
+ public override Transaction ReadJson(JsonReader reader, Type objectType, Transaction existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
+ public override void WriteJson(JsonWriter writer, Transaction value, JsonSerializer serializer)
+ {
+ var j = RestServerUtility.TransactionToJToken(value, serializer);
+ j.WriteTo(writer);
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/UInt160JsonConverter.cs b/src/RestServer/Newtonsoft/Json/UInt160JsonConverter.cs
new file mode 100644
index 000000000..581258efd
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/UInt160JsonConverter.cs
@@ -0,0 +1,39 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class UInt160JsonConverter : JsonConverter
+ {
+ public override bool CanRead => true;
+ public override bool CanWrite => true;
+
+ public override UInt160 ReadJson(JsonReader reader, Type objectType, UInt160 existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var value = reader.Value?.ToString();
+ try
+ {
+ return RestServerUtility.ConvertToScriptHash(value, RestServerPlugin.NeoSystem.Settings);
+ }
+ catch (FormatException)
+ {
+ throw new ScriptHashFormatException($"{value} is invalid.");
+ }
+ }
+
+ public override void WriteJson(JsonWriter writer, UInt160 value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString());
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/UInt256JsonConverter.cs b/src/RestServer/Newtonsoft/Json/UInt256JsonConverter.cs
new file mode 100644
index 000000000..b53814f01
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/UInt256JsonConverter.cs
@@ -0,0 +1,36 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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.Plugins.RestServer.Exceptions;
+using Newtonsoft.Json;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class UInt256JsonConverter : JsonConverter
+ {
+ public override UInt256 ReadJson(JsonReader reader, Type objectType, UInt256 existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var value = reader.Value?.ToString();
+ try
+ {
+ return UInt256.Parse(value);
+ }
+ catch (FormatException)
+ {
+ throw new UInt256FormatException($"{value} is invalid.");
+ }
+ }
+
+ public override void WriteJson(JsonWriter writer, UInt256 value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString());
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmArrayJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmArrayJsonConverter.cs
new file mode 100644
index 000000000..350e65a07
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmArrayJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Array = Neo.VM.Types.Array;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmArrayJsonConverter : JsonConverter
+ {
+ public override Array ReadJson(JsonReader reader, Type objectType, Array existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.Load(reader);
+ return RestServerUtility.StackItemFromJToken(t) as Array;
+ }
+
+ public override void WriteJson(JsonWriter writer, Array value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmBooleanJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmBooleanJsonConverter.cs
new file mode 100644
index 000000000..662af037f
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmBooleanJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Boolean = Neo.VM.Types.Boolean;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmBooleanJsonConverter : JsonConverter
+ {
+ public override Boolean ReadJson(JsonReader reader, Type objectType, Boolean existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.ReadFrom(reader);
+ return RestServerUtility.StackItemFromJToken(t) as Boolean;
+ }
+
+ public override void WriteJson(JsonWriter writer, Boolean value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmBufferJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmBufferJsonConverter.cs
new file mode 100644
index 000000000..628489d6d
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmBufferJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Buffer = Neo.VM.Types.Buffer;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmBufferJsonConverter : JsonConverter
+ {
+ public override Buffer ReadJson(JsonReader reader, Type objectType, Buffer existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.ReadFrom(reader);
+ return RestServerUtility.StackItemFromJToken(t) as Buffer;
+ }
+
+ public override void WriteJson(JsonWriter writer, Buffer value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmByteStringJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmByteStringJsonConverter.cs
new file mode 100644
index 000000000..869484d3f
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmByteStringJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.VM.Types;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmByteStringJsonConverter : JsonConverter
+ {
+ public override ByteString ReadJson(JsonReader reader, Type objectType, ByteString existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.ReadFrom(reader);
+ return RestServerUtility.StackItemFromJToken(t) as ByteString;
+ }
+
+ public override void WriteJson(JsonWriter writer, ByteString value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmIntegerJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmIntegerJsonConverter.cs
new file mode 100644
index 000000000..e30b22a38
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmIntegerJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Integer = Neo.VM.Types.Integer;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmIntegerJsonConverter : JsonConverter
+ {
+ public override Integer ReadJson(JsonReader reader, Type objectType, Integer existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ var t = JToken.ReadFrom(reader);
+ return RestServerUtility.StackItemFromJToken(t) as Integer;
+ }
+
+ public override void WriteJson(JsonWriter writer, Integer value, JsonSerializer serializer)
+ {
+ var t = RestServerUtility.StackItemToJToken(value, null, serializer);
+ t.WriteTo(writer);
+ }
+ }
+}
diff --git a/src/RestServer/Newtonsoft/Json/VmMapJsonConverter.cs b/src/RestServer/Newtonsoft/Json/VmMapJsonConverter.cs
new file mode 100644
index 000000000..ce467347c
--- /dev/null
+++ b/src/RestServer/Newtonsoft/Json/VmMapJsonConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2015-2023 The Neo Project.
+//
+// The Neo.Plugins.RestServer is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.VM.Types;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Neo.Plugins.RestServer.Newtonsoft.Json
+{
+ public class VmMapJsonConverter : JsonConverter