Skip to content

Commit fa5aba3

Browse files
committed
Optimize: clearer parser for parameter Signers and Witnesses, and fix comments
1 parent a0f5ed0 commit fa5aba3

File tree

4 files changed

+251
-126
lines changed

4 files changed

+251
-126
lines changed

src/Plugins/RpcServer/ParameterConverter.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99
// Redistribution and use in source and binary forms with or without
1010
// modifications are permitted.
1111

12+
using Neo.Cryptography.ECC;
13+
using Neo.Extensions;
1214
using Neo.Json;
15+
using Neo.Network.P2P.Payloads;
1316
using Neo.Plugins.RpcServer.Model;
1417
using Neo.Wallets;
1518
using System;
1619
using System.Collections.Generic;
20+
using System.Linq;
1721
using JToken = Neo.Json.JToken;
1822

1923
namespace Neo.Plugins.RpcServer
@@ -138,6 +142,103 @@ private static RpcError CreateInvalidParamError<T>(JToken token)
138142
{
139143
return RpcError.InvalidParams.WithData($"Invalid {typeof(T)} value: {token}");
140144
}
145+
146+
/// <summary>
147+
/// Create a SignersAndWitnesses from a JSON array.
148+
/// Each item in the JSON array should be a JSON object with the following properties:
149+
/// - "signer": A JSON object with the following properties:
150+
/// - "account": A hex-encoded UInt160 or a Base58Check address, required.
151+
/// - "scopes": A enum string representing the scopes(WitnessScope) of the signer, required.
152+
/// - "allowedcontracts": An array of hex-encoded UInt160, optional.
153+
/// - "allowedgroups": An array of hex-encoded ECPoint, optional.
154+
/// - "rules": An array of strings representing the rules(WitnessRule) of the signer, optional.
155+
/// - "witness": A JSON object with the following properties:
156+
/// - "invocation": A base64-encoded string representing the invocation script, optional.
157+
/// - "verification": A base64-encoded string representing the verification script, optional.
158+
/// </summary>
159+
/// <param name="json">The JSON array to create a SignersAndWitnesses from.</param>
160+
/// <param name="addressVersion">The address version to use for the signers.</param>
161+
/// <returns>A SignersAndWitnesses object.</returns>
162+
/// <exception cref="RpcException">Thrown when the JSON array is invalid.</exception>
163+
internal static (Signer[] Signers, Witness[] Witnesses) ToSignersAndWitnesses(this JArray json, byte addressVersion)
164+
{
165+
var signers = json.ToSigners(addressVersion);
166+
var witnesses = json.ToWitnesses();
167+
return new(signers, witnesses);
168+
}
169+
170+
/// <summary>
171+
/// Create a Signer array from a JSON array.
172+
/// Each item in the JSON array should be a JSON object with the following properties:
173+
/// - "account": A hex-encoded UInt160 or a Base58Check address, required.
174+
/// - "scopes": A enum string representing the scopes(WitnessScope) of the signer, required.
175+
/// - "allowedcontracts": An array of hex-encoded UInt160, optional.
176+
/// - "allowedgroups": An array of hex-encoded ECPoint, optional.
177+
/// - "rules": An array of strings representing the rules(WitnessRule) of the signer, optional.
178+
/// </summary>
179+
/// <param name="json">The JSON array to create a Signer array from.</param>
180+
/// <param name="addressVersion">The address version to use for the signers.</param>
181+
/// <returns>A Signer array.</returns>
182+
/// <exception cref="RpcException">Thrown when the JSON array is invalid or max allowed witness exceeded.</exception>
183+
internal static Signer[] ToSigners(this JArray json, byte addressVersion)
184+
{
185+
if (json.Count > Transaction.MaxTransactionAttributes)
186+
throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
187+
188+
var ret = json.Select(u => new Signer
189+
{
190+
Account = u["account"].AsString().AddressToScriptHash(addressVersion),
191+
Scopes = (WitnessScope)Enum.Parse(typeof(WitnessScope), u["scopes"]?.AsString()),
192+
AllowedContracts = ((JArray)u["allowedcontracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray() ?? [],
193+
AllowedGroups = ((JArray)u["allowedgroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray() ?? [],
194+
Rules = ((JArray)u["rules"])?.Select(r => WitnessRule.FromJson((JObject)r)).ToArray() ?? [],
195+
}).ToArray();
196+
197+
// Validate format
198+
_ = ret.ToByteArray().AsSerializableArray<Signer>();
199+
return ret;
200+
}
201+
202+
/// <summary>
203+
/// Create a Witness array from a JSON array.
204+
/// Each item in the JSON array should be a JSON object with the following properties:
205+
/// - "invocation": A base64-encoded string representing the invocation script, optional.
206+
/// - "verification": A base64-encoded string representing the verification script, optional.
207+
/// </summary>
208+
/// <param name="json">The JSON array to create a Witness array from.</param>
209+
/// <returns>A Witness array.</returns>
210+
/// <exception cref="RpcException">Thrown when the JSON array is invalid or max allowed witness exceeded.</exception>
211+
internal static Witness[] ToWitnesses(this JArray json)
212+
{
213+
if (json.Count > Transaction.MaxTransactionAttributes)
214+
throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
215+
216+
return json.Select(u => new
217+
{
218+
Invocation = u["invocation"]?.AsString(),
219+
Verification = u["verification"]?.AsString()
220+
})
221+
.Where(x => x.Invocation != null || x.Verification != null)
222+
.Select(x => new Witness()
223+
{
224+
InvocationScript = Convert.FromBase64String(x.Invocation ?? string.Empty),
225+
VerificationScript = Convert.FromBase64String(x.Verification ?? string.Empty)
226+
})
227+
.ToArray();
228+
}
229+
230+
/// <summary>
231+
/// Converts an hex-encoded UInt160 or a Base58Check address to a script hash.
232+
/// </summary>
233+
/// <param name="address">The address to convert.</param>
234+
/// <param name="version">The address version to use for the conversion.</param>
235+
/// <returns>The script hash corresponding to the address.</returns>
236+
internal static UInt160 AddressToScriptHash(this string address, byte version)
237+
{
238+
if (UInt160.TryParse(address, out var scriptHash))
239+
return scriptHash;
240+
return address.ToScriptHash(version);
241+
}
141242
}
142243

143244
public static class TypeExtensions

src/Plugins/RpcServer/RpcServer.SmartContract.cs

Lines changed: 42 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Redistribution and use in source and binary forms with or without
1010
// modifications are permitted.
1111

12-
using Neo.Cryptography.ECC;
1312
using Neo.Extensions;
1413
using Neo.Json;
1514
using Neo.Network.P2P.Payloads;
@@ -24,7 +23,6 @@
2423
using System.Collections.Generic;
2524
using System.Linq;
2625
using System.Threading;
27-
using Array = System.Array;
2826

2927
namespace Neo.Plugins.RpcServer
3028
{
@@ -171,47 +169,6 @@ private static JObject ToJson(StackItem item, Session session)
171169
return json;
172170
}
173171

174-
private static Signer[] SignersFromJson(JArray _params, ProtocolSettings settings)
175-
{
176-
if (_params.Count > Transaction.MaxTransactionAttributes)
177-
{
178-
throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
179-
}
180-
181-
var ret = _params.Select(u => new Signer
182-
{
183-
Account = AddressToScriptHash(u["account"].AsString(), settings.AddressVersion),
184-
Scopes = (WitnessScope)Enum.Parse(typeof(WitnessScope), u["scopes"]?.AsString()),
185-
AllowedContracts = ((JArray)u["allowedcontracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray() ?? Array.Empty<UInt160>(),
186-
AllowedGroups = ((JArray)u["allowedgroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray() ?? Array.Empty<ECPoint>(),
187-
Rules = ((JArray)u["rules"])?.Select(r => WitnessRule.FromJson((JObject)r)).ToArray() ?? Array.Empty<WitnessRule>(),
188-
}).ToArray();
189-
190-
// Validate format
191-
192-
_ = ret.ToByteArray().AsSerializableArray<Signer>();
193-
194-
return ret;
195-
}
196-
197-
private static Witness[] WitnessesFromJson(JArray _params)
198-
{
199-
if (_params.Count > Transaction.MaxTransactionAttributes)
200-
{
201-
throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
202-
}
203-
204-
return _params.Select(u => new
205-
{
206-
Invocation = u["invocation"]?.AsString(),
207-
Verification = u["verification"]?.AsString()
208-
}).Where(x => x.Invocation != null || x.Verification != null).Select(x => new Witness()
209-
{
210-
InvocationScript = Convert.FromBase64String(x.Invocation ?? string.Empty),
211-
VerificationScript = Convert.FromBase64String(x.Verification ?? string.Empty)
212-
}).ToArray();
213-
}
214-
215172
/// <summary>
216173
/// Invokes a function on a contract.
217174
/// <para>Request format:</para>
@@ -224,13 +181,16 @@ private static Witness[] WitnessesFromJson(JArray _params)
224181
/// "operation", // the operation to invoke
225182
/// [{"type": "ContractParameterType", "value": "The parameter value"}], // ContractParameter, the arguments
226183
/// [{
227-
/// "account": "An UInt160 or Base58Check address",
228-
/// "scopes": "WitnessScope", // WitnessScope
184+
/// // The part of the Signer
185+
/// "account": "An UInt160 or Base58Check address", // The account of the signer, required
186+
/// "scopes": "WitnessScope", // WitnessScope, required
229187
/// "allowedcontracts": ["The contract hash(UInt160)"], // optional
230188
/// "allowedgroups": ["PublicKey"], // ECPoint, i.e. ECC PublicKey, optional
231189
/// "rules": [{"action": "WitnessRuleAction", "condition": {/*A json of WitnessCondition*/}}] // WitnessRule
232-
/// }], // A Signer array, optional
233-
/// [{"invocation": "A Base64-encoded string","verification": "A Base64-encoded string"}] // A Witness array, optional
190+
/// // The part of the Witness, optional
191+
/// "invocation": "A Base64-encoded string",
192+
/// "verification": "A Base64-encoded string"
193+
/// }], // A JSON array of signers and witnesses, optional
234194
/// false // useDiagnostic, a bool value indicating whether to use diagnostic information, optional
235195
/// ]
236196
/// }</code>
@@ -266,9 +226,8 @@ private static Witness[] WitnessesFromJson(JArray _params)
266226
/// [0]: The script hash of the contract to invoke as a string.
267227
/// [1]: The operation to invoke as a string.
268228
/// [2]: The arguments to pass to the function as an array of ContractParameter. Optional.
269-
/// [3]: The signers as an array of Signer. Optional.
270-
/// [4]: The witnesses as an array of Witness. Optional.
271-
/// [5]: A boolean value indicating whether to use diagnostic information. Optional.
229+
/// [3]: The JSON array of signers and witnesses<see cref="ParameterConverter.ToSignersAndWitnesses"/>. Optional.
230+
/// [4]: A boolean value indicating whether to use diagnostic information. Optional.
272231
/// </param>
273232
/// <returns>The result of the function invocation.</returns>
274233
/// <exception cref="RpcException">
@@ -277,17 +236,24 @@ private static Witness[] WitnessesFromJson(JArray _params)
277236
[RpcMethod]
278237
protected internal virtual JToken InvokeFunction(JArray _params)
279238
{
280-
UInt160 script_hash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash {nameof(script_hash)}"));
281-
string operation = Result.Ok_Or(() => _params[1].AsString(), RpcError.InvalidParams);
282-
ContractParameter[] args = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray() : [];
283-
Signer[] signers = _params.Count >= 4 ? SignersFromJson((JArray)_params[3], system.Settings) : null;
284-
Witness[] witnesses = _params.Count >= 4 ? WitnessesFromJson((JArray)_params[3]) : null;
285-
bool useDiagnostic = _params.Count >= 5 && _params[4].GetBoolean();
239+
var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()),
240+
RpcError.InvalidParams.WithData($"Invalid script hash `{_params[0]}`"));
241+
242+
var operation = Result.Ok_Or(() => _params[1].AsString(), RpcError.InvalidParams);
243+
var args = _params.Count >= 3
244+
? ((JArray)_params[2]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray()
245+
: [];
246+
247+
var (signers, witnesses) = _params.Count >= 4
248+
? ((JArray)_params[3]).ToSignersAndWitnesses(system.Settings.AddressVersion)
249+
: (null, null);
250+
251+
var useDiagnostic = _params.Count >= 5 && _params[4].GetBoolean();
286252

287253
byte[] script;
288254
using (ScriptBuilder sb = new())
289255
{
290-
script = sb.EmitDynamicCall(script_hash, operation, args).ToArray();
256+
script = sb.EmitDynamicCall(scriptHash, operation, args).ToArray();
291257
}
292258
return GetInvokeResult(script, signers, witnesses, useDiagnostic);
293259
}
@@ -302,13 +268,16 @@ protected internal virtual JToken InvokeFunction(JArray _params)
302268
/// "params": [
303269
/// "A Base64-encoded script", // the script to invoke
304270
/// [{
305-
/// "account": "An UInt160 or Base58Check address",
306-
/// "scopes": "WitnessScope", // WitnessScope
271+
/// // The part of the Signer
272+
/// "account": "An UInt160 or Base58Check address", // The account of the signer, required
273+
/// "scopes": "WitnessScope", // WitnessScope, required
307274
/// "allowedcontracts": ["The contract hash(UInt160)"], // optional
308275
/// "allowedgroups": ["PublicKey"], // ECPoint, i.e. ECC PublicKey, optional
309-
/// "rules": [{"action": "WitnessRuleAction", "condition": {A json of WitnessCondition}}] // WitnessRule
310-
/// }], // A Signer array, optional
311-
/// [{"invocation": "A Base64-encoded string","verification": "A Base64-encoded string"}] // A Witness array, optional
276+
/// "rules": [{"action": "WitnessRuleAction", "condition": {/* A json of WitnessCondition */ }}], // WitnessRule
277+
/// // The part of the Witness, optional
278+
/// "invocation": "A Base64-encoded string",
279+
/// "verification": "A Base64-encoded string"
280+
/// }], // A JSON array of signers and witnesses, optional
312281
/// false // useDiagnostic, a bool value indicating whether to use diagnostic information, optional
313282
/// ]
314283
/// }</code>
@@ -348,8 +317,7 @@ protected internal virtual JToken InvokeFunction(JArray _params)
348317
/// </summary>
349318
/// <param name="_params">An array containing the following elements:
350319
/// [0]: The script as a Base64-encoded string.
351-
/// [1]: The signers as an array of Signer. Optional.
352-
/// [2]: The witnesses as an array of Witness. Optional.
320+
/// [1]: The JSON array of signers and witnesses<see cref="ParameterConverter.ToSignersAndWitnesses"/>. Optional.
353321
/// [3]: A boolean value indicating whether to use diagnostic information. Optional.
354322
/// </param>
355323
/// <returns>The result of the script invocation.</returns>
@@ -359,10 +327,11 @@ protected internal virtual JToken InvokeFunction(JArray _params)
359327
[RpcMethod]
360328
protected internal virtual JToken InvokeScript(JArray _params)
361329
{
362-
byte[] script = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams);
363-
Signer[] signers = _params.Count >= 2 ? SignersFromJson((JArray)_params[1], system.Settings) : null;
364-
Witness[] witnesses = _params.Count >= 2 ? WitnessesFromJson((JArray)_params[1]) : null;
365-
bool useDiagnostic = _params.Count >= 3 && _params[2].GetBoolean();
330+
var script = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams);
331+
var (signers, witnesses) = _params.Count >= 2
332+
? ((JArray)_params[1]).ToSignersAndWitnesses(system.Settings.AddressVersion)
333+
: (null, null);
334+
var useDiagnostic = _params.Count >= 3 && _params[2].GetBoolean();
366335
return GetInvokeResult(script, signers, witnesses, useDiagnostic);
367336
}
368337

@@ -469,9 +438,12 @@ protected internal virtual JToken TerminateSession(JArray _params)
469438
[RpcMethod]
470439
protected internal virtual JToken GetUnclaimedGas(JArray _params)
471440
{
472-
string address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invalid address {nameof(address)}"));
441+
var address = Result.Ok_Or(() => _params[0].AsString(),
442+
RpcError.InvalidParams.WithData($"Invalid address `{_params[0]}`"));
473443
var json = new JObject();
474-
UInt160 scriptHash = Result.Ok_Or(() => AddressToScriptHash(address, system.Settings.AddressVersion), RpcError.InvalidParams);
444+
var scriptHash = Result.Ok_Or(
445+
() => address.AddressToScriptHash(system.Settings.AddressVersion),
446+
RpcError.InvalidParams.WithData($"Invalid address `{address}`"));
475447

476448
var snapshot = system.StoreView;
477449
json["unclaimed"] = NativeContract.NEO.UnclaimedGas(snapshot, scriptHash, NativeContract.Ledger.CurrentIndex(snapshot) + 1).ToString();

0 commit comments

Comments
 (0)