Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert InteropInterface<IEnumerator> to Array for RpcClient to digest #245

Closed
wants to merge 13 commits into from
82 changes: 74 additions & 8 deletions src/RpcServer/RpcServer.SmartContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
#pragma warning disable IDE0060

using Neo.IO.Json;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using Neo.VM.Types;
using Neo.Wallets;
using System;
using System.IO;
using System.Linq;
using Neo.IO;
using Neo.Ledger;
using Neo.SmartContract.Native;
using Neo.Wallets;
using Array = Neo.VM.Types.Array;

namespace Neo.Plugins
{
Expand Down Expand Up @@ -55,7 +56,9 @@ public void SerializeUnsigned(BinaryWriter writer)
}
}

private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = null)
private const int ResultsPerPage = 50;

private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = null, int page = 0)
{
using ApplicationEngine engine = ApplicationEngine.Run(script, checkWitnessHashes, extraGAS: settings.MaxGasInvoke);
JObject json = new JObject();
Expand All @@ -64,7 +67,9 @@ private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes =
json["gas_consumed"] = engine.GasConsumed.ToString();
try
{
json["stack"] = new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()));
var stackItems = engine.ResultStack.ToArray();
ConvertIEnumeratorToArray(stackItems, page); // convert InteropInterface<IEnumerator> to Array for RpcClient to digest
json["stack"] = new JArray(stackItems.Select(p => p.ToJson()));
}
catch (InvalidOperationException)
{
Expand All @@ -74,6 +79,65 @@ private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes =
return json;
}

public static void ConvertIEnumeratorToArray(StackItem[] stackItems, int page = 0)
{
for (int i = 0; i < stackItems.Length; i++)
{
if (stackItems[i] is InteropInterface interopInterface)
{
if (interopInterface.TryGetInterface(out System.Collections.IEnumerator sysEnum))
{
Array array = new Array();
int index = 0;
int low = page * ResultsPerPage; // 0, 50, 100...
int high = (page + 1) * ResultsPerPage - 1; // 49, 99, 149...
while (sysEnum.MoveNext() && index <= high)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxPage in config?

if (index >= low)
{
StackItem current = null;
var obj = sysEnum.Current;
try
{
current = (StackItem)obj;
}
catch (InvalidCastException)
{
}
if (!(current is StackItem))
{
if (obj is IInteroperable interoperable)
current = interoperable.ToStackItem(null);
else
current = new InteropInterface(obj);
}
array.Add(current);
}
index++;
}
stackItems[i] = array;
continue;
}
if (interopInterface.TryGetInterface(out Neo.SmartContract.Enumerators.IEnumerator neoEnum))
{
Array array = new Array();
int index = 0;
int low = page * ResultsPerPage; // 0, 50, 100...
int high = (page + 1) * ResultsPerPage - 1; // 49, 99, 149...
while (neoEnum.Next() && index <= high)
{
if (index >= low)
{
array.Add(neoEnum.Value());
}
index++;
}
stackItems[i] = array;
}
}
}
}

[RpcMethod]
private JObject InvokeFunction(JArray _params)
{
Expand All @@ -86,15 +150,17 @@ private JObject InvokeFunction(JArray _params)
{
script = sb.EmitAppCall(script_hash, operation, args).ToArray();
}
return GetInvokeResult(script, checkWitnessHashes);
int page = _params.Count >= 5 ? int.Parse(_params[4].AsString()) : 0;
return GetInvokeResult(script, checkWitnessHashes, page);
}

[RpcMethod]
private JObject InvokeScript(JArray _params)
{
byte[] script = _params[0].AsString().HexToBytes();
CheckWitnessHashes checkWitnessHashes = _params.Count >= 2 ? new CheckWitnessHashes(((JArray)_params[1]).Select(u => UInt160.Parse(u.AsString())).ToArray()) : null;
return GetInvokeResult(script, checkWitnessHashes);
int page = _params.Count >= 3 ? int.Parse(_params[4].AsString()) : 0;
return GetInvokeResult(script, checkWitnessHashes, page);
}

[RpcMethod]
Expand Down
2 changes: 1 addition & 1 deletion src/RpcServer/RpcServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Neo" Version="3.0.0-CI00935" />
<PackageReference Include="Neo" Version="3.0.0-CI00940" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<ItemGroup>
<ProjectReference Include="..\..\src\RpcClient\RpcClient.csproj" />
<ProjectReference Include="..\..\src\RpcServer\RpcServer.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
93 changes: 93 additions & 0 deletions tests/Neo.Network.RPC.Tests/UT_RpcServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.IO;
using Neo.Plugins;
using Neo.SmartContract.Iterators;
using Neo.SmartContract.Native.Tokens;
using Neo.VM.Types;
using Neo.Wallets;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Neo.Network.RPC.Tests
{
[TestClass]
public class UT_RpcServer
{
[TestMethod]
public void Test_ConvertIEnumeratorToArray()
{
var a = new byte[100][];
for (int i = 0; i < a.Length; i++)
{
a[i] = "NeHNBbeLNtiCEeaFQ6tLLpXkr5Xw6esKnV".ToScriptHash().ToArray();
}
IReadOnlyList<byte[]> values = a;

IIterator iterator = new TestIterator(values);

StackItem[] stackItems = new StackItem[]
{
12345,
"hello",
new InteropInterface(Enumerable.Range(1, 100).GetEnumerator()),
new InteropInterface(new AccountState[] {new AccountState()}.GetEnumerator()),
new InteropInterface(iterator)
};

Assert.AreEqual(StackItemType.InteropInterface, stackItems[2].Type);
Assert.AreEqual(StackItemType.InteropInterface, stackItems[3].Type);
Assert.AreEqual(StackItemType.InteropInterface, stackItems[4].Type);

RpcServer.ConvertIEnumeratorToArray(stackItems, 1);

Assert.AreEqual(StackItemType.Array, stackItems[2].Type);
Assert.AreEqual(50, ((VM.Types.Array)stackItems[2]).Count);
Assert.AreEqual(new InteropInterface(51), ((VM.Types.Array)stackItems[2])[0]);

Assert.AreEqual(StackItemType.Array, stackItems[3].Type);

Assert.AreEqual(StackItemType.Array, stackItems[4].Type);
Assert.AreEqual(50, ((VM.Types.Array)stackItems[4]).Count);
Assert.AreEqual(new ByteString("NeHNBbeLNtiCEeaFQ6tLLpXkr5Xw6esKnV".ToScriptHash().ToArray()), ((VM.Types.Array)stackItems[4])[0]);
}
}

internal class TestIterator : IIterator
{
private readonly IReadOnlyList<byte[]> values;
private int index = -1;

public TestIterator(IReadOnlyList<byte[]> vs)
{
this.values = vs;
}

public PrimitiveType Key()
{
if (index < 0)
throw new InvalidOperationException();
return index;
}

public bool Next()
{
int next = index + 1;
if (next >= values.Count)
return false;
index = next;
return true;
}

public StackItem Value()
{
if (index < 0)
throw new InvalidOperationException();
return values[index];
}

public void Dispose()
{
}
}
}
1 change: 1 addition & 0 deletions tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public void TestSign()
Assert.IsTrue(Crypto.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey));
// verify network fee and system fee
long networkFee = tx.Size * (long)1000 + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.ECDsaVerifyPrice * 1;

Assert.AreEqual(networkFee, tx.NetworkFee);
Assert.AreEqual(100, tx.SystemFee);

Expand Down