diff --git a/src/Neo.CLI/config.json b/src/Neo.CLI/config.json index 772a221714..ccce32d36c 100644 --- a/src/Neo.CLI/config.json +++ b/src/Neo.CLI/config.json @@ -6,7 +6,7 @@ "Active": false }, "Storage": { - "Engine": "LevelDBStore", + "Engine": "MemoryStore", "Path": "Data_LevelDB_{0}" }, "P2P": { diff --git a/src/Plugins/RestServer/Controllers/v1/ContractsController.cs b/src/Plugins/RestServer/Controllers/v1/ContractsController.cs index ede2fc4f3b..4d61840d16 100644 --- a/src/Plugins/RestServer/Controllers/v1/ContractsController.cs +++ b/src/Plugins/RestServer/Controllers/v1/ContractsController.cs @@ -15,6 +15,7 @@ using Neo.Plugins.RestServer.Extensions; using Neo.Plugins.RestServer.Helpers; using Neo.Plugins.RestServer.Models; +using Neo.Plugins.RestServer.Models.Contract; using Neo.Plugins.RestServer.Models.Error; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -187,7 +188,7 @@ public IActionResult GetContractNef( /// /// ScriptHash /// method name - /// JArray of the contract parameters. + /// JArray of the contract parameters. /// Execution Engine object. /// Successful /// An error occurred. See Response for details. @@ -199,7 +200,7 @@ public IActionResult InvokeContract( [FromQuery(Name = "method")] string method, [FromBody] - ContractParameter[] contractParameters) + InvokeParams invokeParameters) { var contracts = NativeContract.ContractManagement.GetContract(_neoSystem.StoreView, scriptHash); if (contracts == null) @@ -208,7 +209,7 @@ public IActionResult InvokeContract( throw new QueryParameterNotFoundException(nameof(method)); try { - var engine = ScriptHelper.InvokeMethod(_neoSystem.Settings, _neoSystem.StoreView, contracts.Hash, method, contractParameters, out var script); + var engine = ScriptHelper.InvokeMethod(_neoSystem.Settings, _neoSystem.StoreView, contracts.Hash, method, invokeParameters.ContractParameters, invokeParameters.Signers, out var script); return Ok(engine.ToModel()); } catch (Exception ex) diff --git a/src/Plugins/RestServer/Controllers/v1/NodeController.cs b/src/Plugins/RestServer/Controllers/v1/NodeController.cs index 447173e6b2..7d51c5a87b 100644 --- a/src/Plugins/RestServer/Controllers/v1/NodeController.cs +++ b/src/Plugins/RestServer/Controllers/v1/NodeController.cs @@ -30,13 +30,13 @@ namespace Neo.Plugins.RestServer.Controllers.v1 [ApiController] public class NodeController : ControllerBase { - private readonly LocalNode _neolocalnode; - private readonly NeoSystem _neosystem; + private readonly LocalNode _neoLocalNode; + private readonly NeoSystem _neoSystem; public NodeController() { - _neolocalnode = RestServerPlugin.LocalNode ?? throw new InvalidOperationException(); - _neosystem = RestServerPlugin.NeoSystem ?? throw new InvalidOperationException(); + _neoLocalNode = RestServerPlugin.LocalNode ?? throw new InvalidOperationException(); + _neoSystem = RestServerPlugin.NeoSystem ?? throw new InvalidOperationException(); } /// @@ -49,7 +49,7 @@ public NodeController() [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(RemoteNodeModel[]))] public IActionResult GetPeers() { - var rNodes = _neolocalnode + var rNodes = _neoLocalNode .GetRemoteNodes() .OrderByDescending(o => o.LastBlockIndex) .ToArray(); @@ -83,6 +83,6 @@ public IActionResult GetPlugins() => [HttpGet("settings", Name = "GetNodeProtocolSettings")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ProtocolSettingsModel))] public IActionResult GetSettings() => - Ok(_neosystem.Settings.ToModel()); + Ok(_neoSystem.Settings.ToModel()); } } diff --git a/src/Plugins/RestServer/Controllers/v1/TokensController.cs b/src/Plugins/RestServer/Controllers/v1/TokensController.cs index cbdebc4a20..78078b7cd3 100644 --- a/src/Plugins/RestServer/Controllers/v1/TokensController.cs +++ b/src/Plugins/RestServer/Controllers/v1/TokensController.cs @@ -34,11 +34,11 @@ namespace Neo.Plugins.RestServer.Controllers.v1 [ApiController] public class TokensController : ControllerBase { - private readonly NeoSystem _neosystem; + private readonly NeoSystem _neoSystem; public TokensController() { - _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException(); + _neoSystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException(); } #region NEP-17 @@ -63,7 +63,7 @@ public IActionResult GetNEP17( { if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize) throw new InvalidParameterRangeException(); - var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView); + var tokenList = NativeContract.ContractManagement.ListContracts(_neoSystem.StoreView); var vaildContracts = tokenList .Where(ContractHelper.IsNep17Supported) .OrderBy(o => o.Manifest.Name) @@ -76,7 +76,7 @@ public IActionResult GetNEP17( { try { - var token = new NEP17Token(_neosystem, contract.Hash); + var token = new NEP17Token(_neoSystem, contract.Hash); listResults.Add(token.ToModel()); } catch @@ -100,7 +100,7 @@ public IActionResult GetNEP17Count() { return Ok(new CountModel() { - Count = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView).Count(ContractHelper.IsNep17Supported) + Count = NativeContract.ContractManagement.ListContracts(_neoSystem.StoreView).Count(ContractHelper.IsNep17Supported) }); } @@ -120,13 +120,13 @@ public IActionResult GetNEP17( [FromRoute(Name = "address")] UInt160 lookupAddressOrScripthash) { - var contract = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, tokenAddessOrScripthash) ?? + var contract = NativeContract.ContractManagement.GetContract(_neoSystem.StoreView, tokenAddessOrScripthash) ?? throw new ContractNotFoundException(tokenAddessOrScripthash); if (ContractHelper.IsNep17Supported(contract) == false) throw new Nep17NotSupportedException(tokenAddessOrScripthash); try { - var token = new NEP17Token(_neosystem, tokenAddessOrScripthash); + var token = new NEP17Token(_neoSystem, tokenAddessOrScripthash); return Ok(new TokenBalanceModel() { Name = token.Name, @@ -167,7 +167,7 @@ public IActionResult GetNEP11( { if (skip < 1 || take < 1 || take > RestServerSettings.Current.MaxPageSize) throw new InvalidParameterRangeException(); - var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView); + var tokenList = NativeContract.ContractManagement.ListContracts(_neoSystem.StoreView); var vaildContracts = tokenList .Where(ContractHelper.IsNep11Supported) .OrderBy(o => o.Manifest.Name) @@ -180,7 +180,7 @@ public IActionResult GetNEP11( { try { - var token = new NEP11Token(_neosystem, contract.Hash); + var token = new NEP11Token(_neoSystem, contract.Hash); listResults.Add(token.ToModel()); } catch @@ -202,7 +202,7 @@ public IActionResult GetNEP11( [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CountModel))] public IActionResult GetNEP11Count() { - return Ok(new CountModel() { Count = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView).Count(ContractHelper.IsNep11Supported) }); + return Ok(new CountModel() { Count = NativeContract.ContractManagement.ListContracts(_neoSystem.StoreView).Count(ContractHelper.IsNep11Supported) }); } /// @@ -221,13 +221,13 @@ public IActionResult GetNEP11( [FromRoute(Name = "address")] UInt160 addressHash) { - var contract = NativeContract.ContractManagement.GetContract(_neosystem.StoreView, sAddressHash) ?? + var contract = NativeContract.ContractManagement.GetContract(_neoSystem.StoreView, sAddressHash) ?? throw new ContractNotFoundException(sAddressHash); if (ContractHelper.IsNep11Supported(contract) == false) throw new Nep11NotSupportedException(sAddressHash); try { - var token = new NEP11Token(_neosystem, sAddressHash); + var token = new NEP11Token(_neoSystem, sAddressHash); return Ok(new TokenBalanceModel() { Name = token.Name, @@ -259,7 +259,7 @@ public IActionResult GetBalances( [FromRoute(Name = "address")] UInt160 addressOrScripthash) { - var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView); + var tokenList = NativeContract.ContractManagement.ListContracts(_neoSystem.StoreView); var validContracts = tokenList .Where(w => ContractHelper.IsNep17Supported(w) || ContractHelper.IsNep11Supported(w)) .OrderBy(o => o.Manifest.Name); @@ -268,7 +268,7 @@ public IActionResult GetBalances( { try { - var token = new NEP17Token(_neosystem, contract.Hash); + var token = new NEP17Token(_neoSystem, contract.Hash); var balance = token.BalanceOf(addressOrScripthash).Value; if (balance == 0) continue; @@ -282,7 +282,7 @@ public IActionResult GetBalances( TotalSupply = token.TotalSupply().Value, }); - var nft = new NEP11Token(_neosystem, contract.Hash); + var nft = new NEP11Token(_neoSystem, contract.Hash); balance = nft.BalanceOf(addressOrScripthash).Value; if (balance == 0) continue; diff --git a/src/Plugins/RestServer/Controllers/v1/UtilsController.cs b/src/Plugins/RestServer/Controllers/v1/UtilsController.cs index afd5df92c0..88d03d0fca 100644 --- a/src/Plugins/RestServer/Controllers/v1/UtilsController.cs +++ b/src/Plugins/RestServer/Controllers/v1/UtilsController.cs @@ -28,11 +28,11 @@ namespace Neo.Plugins.RestServer.Controllers.v1 [ApiController] public class UtilsController : ControllerBase { - private readonly NeoSystem _neosystem; + private readonly NeoSystem _neoSystem; public UtilsController() { - _neosystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException(); + _neoSystem = RestServerPlugin.NeoSystem ?? throw new NodeNetworkException(); } #region Validation @@ -52,7 +52,7 @@ public IActionResult ScriptHashToWalletAddress( { try { - return Ok(new UtilsAddressModel() { Address = ScriptHash.ToAddress(_neosystem.Settings.AddressVersion) }); + return Ok(new UtilsAddressModel() { Address = ScriptHash.ToAddress(_neoSystem.Settings.AddressVersion) }); } catch (FormatException) { @@ -75,7 +75,7 @@ public IActionResult WalletAddressToScriptHash( { try { - return Ok(new UtilsScriptHashModel() { ScriptHash = address.ToScriptHash(_neosystem.Settings.AddressVersion) }); + return Ok(new UtilsScriptHashModel() { ScriptHash = address.ToScriptHash(_neoSystem.Settings.AddressVersion) }); } catch (FormatException) { @@ -99,7 +99,7 @@ public IActionResult ValidateAddress( return Ok(new UtilsAddressIsValidModel() { Address = AddressOrScriptHash, - IsValid = RestServerUtility.TryConvertToScriptHash(AddressOrScriptHash, _neosystem.Settings, out _), + IsValid = RestServerUtility.TryConvertToScriptHash(AddressOrScriptHash, _neoSystem.Settings, out _), }); } diff --git a/src/Plugins/RestServer/Helpers/ScriptHelper.cs b/src/Plugins/RestServer/Helpers/ScriptHelper.cs index 955993d277..d592ef8708 100644 --- a/src/Plugins/RestServer/Helpers/ScriptHelper.cs +++ b/src/Plugins/RestServer/Helpers/ScriptHelper.cs @@ -16,6 +16,7 @@ using Neo.VM; using Neo.VM.Types; using System; +using System.Linq; namespace Neo.Plugins.RestServer.Helpers { @@ -31,11 +32,21 @@ public static bool InvokeMethod(ProtocolSettings protocolSettings, DataCache sna return engine.State == VMState.HALT; } - public static ApplicationEngine InvokeMethod(ProtocolSettings protocolSettings, DataCache snapshot, UInt160 scriptHash, string method, ContractParameter[] args, out byte[] script) + public static ApplicationEngine InvokeMethod(ProtocolSettings protocolSettings, DataCache snapshot, UInt160 scriptHash, string method, ContractParameter[] args, Signer[]? signers, out byte[] script) { using var scriptBuilder = new ScriptBuilder(); scriptBuilder.EmitDynamicCall(scriptHash, method, CallFlags.ReadOnly, args); script = scriptBuilder.ToArray(); + var tx = signers == null ? null : new Transaction + { + Version = 0, + Nonce = (uint)Random.Shared.Next(), + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + protocolSettings.MaxValidUntilBlockIncrement, + Signers = signers, + Attributes = [], + Script = script, + Witnesses = [.. signers.Select(s => new Witness())], + }; using var engine = ApplicationEngine.Run(script, snapshot, settings: protocolSettings, gas: RestServerSettings.Current.MaxGasInvoke); return engine; } @@ -45,7 +56,7 @@ public static ApplicationEngine InvokeScript(ReadOnlyMemory script, Signer var neoSystem = RestServerPlugin.NeoSystem ?? throw new InvalidOperationException(); var snapshot = neoSystem.GetSnapshotCache(); - Transaction? tx = signers == null ? null : new Transaction + var tx = signers == null ? null : new Transaction { Version = 0, Nonce = (uint)Random.Shared.Next(), diff --git a/src/Plugins/RestServer/Models/Contract/InvokeParams.cs b/src/Plugins/RestServer/Models/Contract/InvokeParams.cs new file mode 100644 index 0000000000..f455e19d7e --- /dev/null +++ b/src/Plugins/RestServer/Models/Contract/InvokeParams.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InvokeParams.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; + +namespace Neo.Plugins.RestServer.Models.Contract +{ + public class InvokeParams + { + public ContractParameter[] ContractParameters { get; set; } = []; + public Signer[] Signers { get; set; } = []; + } +} diff --git a/src/Plugins/RestServer/Newtonsoft/Json/ContractInvokeParametersJsonConverter.cs b/src/Plugins/RestServer/Newtonsoft/Json/ContractInvokeParametersJsonConverter.cs new file mode 100644 index 0000000000..e455cef718 --- /dev/null +++ b/src/Plugins/RestServer/Newtonsoft/Json/ContractInvokeParametersJsonConverter.cs @@ -0,0 +1,35 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractInvokeParametersJsonConverter.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Plugins.RestServer.Models.Contract; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace Neo.Plugins.RestServer.Newtonsoft.Json +{ + public class ContractInvokeParametersJsonConverter : JsonConverter + { + public override bool CanRead => true; + public override bool CanWrite => false; + + public override InvokeParams ReadJson(JsonReader reader, Type objectType, InvokeParams? existingValue, bool hasExistingValue, global::Newtonsoft.Json.JsonSerializer serializer) + { + var token = JToken.ReadFrom(reader); + return RestServerUtility.ContractInvokeParametersFromJToken(token); + } + + public override void WriteJson(JsonWriter writer, InvokeParams? value, global::Newtonsoft.Json.JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Plugins/RestServer/RestServerSettings.cs b/src/Plugins/RestServer/RestServerSettings.cs index de95f68eda..38c886941b 100644 --- a/src/Plugins/RestServer/RestServerSettings.cs +++ b/src/Plugins/RestServer/RestServerSettings.cs @@ -78,8 +78,8 @@ public class RestServerSettings MissingMemberHandling = MissingMemberHandling.Error, NullValueHandling = NullValueHandling.Include, Formatting = Formatting.None, - Converters = new JsonConverter[] - { + Converters = + [ new StringEnumConverter(), new BigDecimalJsonConverter(), new BlockHeaderJsonConverter(), @@ -87,6 +87,7 @@ public class RestServerSettings new ContractAbiJsonConverter(), new ContractEventDescriptorJsonConverter(), new ContractGroupJsonConverter(), + new ContractInvokeParametersJsonConverter(), new ContractJsonConverter(), new ContractManifestJsonConverter(), new ContractMethodJsonConverter(), @@ -119,7 +120,7 @@ public class RestServerSettings new WitnessConditionJsonConverter(), new WitnessJsonConverter(), new WitnessRuleJsonConverter(), - }, + ], }, }; diff --git a/src/Plugins/RestServer/RestServerUtility.cs b/src/Plugins/RestServer/RestServerUtility.cs index 3b5172a503..53bf798c17 100644 --- a/src/Plugins/RestServer/RestServerUtility.cs +++ b/src/Plugins/RestServer/RestServerUtility.cs @@ -10,6 +10,8 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using Neo.Plugins.RestServer.Models.Contract; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -246,6 +248,53 @@ public static JToken StackItemToJToken(StackItem item, IList<(StackItem, JToken? return o; } + public static InvokeParams ContractInvokeParametersFromJToken(JToken token) + { + if (token is null) + throw new ArgumentNullException(); + if (token.Type != JTokenType.Object) + throw new FormatException(); + + var obj = (JObject)token; + var contractParametersProp = obj + .Properties() + .SingleOrDefault(a => a.Name.Equals("contractParameters", StringComparison.InvariantCultureIgnoreCase)); + var signersProp = obj + .Properties() + .SingleOrDefault(a => a.Name.Equals("signers", StringComparison.InvariantCultureIgnoreCase)); + + return new() + { + ContractParameters = [.. contractParametersProp!.Value.Select(ContractParameterFromJToken)], + Signers = [.. signersProp!.Value.Select(SignerFromJToken)], + }; + } + + public static Signer SignerFromJToken(JToken? token) + { + if (token is null) + throw new ArgumentNullException(); + if (token.Type != JTokenType.Object) + throw new FormatException(); + + var obj = (JObject)token; + var accountProp = obj + .Properties() + .SingleOrDefault(a => a.Name.Equals("account", StringComparison.InvariantCultureIgnoreCase)); + var scopesProp = obj + .Properties() + .SingleOrDefault(a => a.Name.Equals("scopes", StringComparison.InvariantCultureIgnoreCase)); + + if (accountProp == null || scopesProp == null) + throw new FormatException(); + + return new() + { + Account = UInt160.Parse(accountProp.ToObject()), + Scopes = Enum.Parse(scopesProp.ToObject()!), + }; + } + public static ContractParameter ContractParameterFromJToken(JToken? token) { if (token is null)