Skip to content

Commit

Permalink
RpcClient missing state root related methods (#644)
Browse files Browse the repository at this point in the history
* RpcClient missing state root related methods
Fixes #635

* fix formatting

* Add RpcSend/Send methods (fixes #646)

* update names to match rpc endpoint

* update to latest state api

* fix formatting

* use span/memory instead of array where possible

* add EnumerateFindStatesAsync and fix MakeFindStatesParams

* Adapt to latest state api changes

* fix formatting

* CR Feedback

* rename

* rename

* fix formatting <grrr>

* PR Feedback

Co-authored-by: Owen Zhang <[email protected]>
Co-authored-by: Harry Pierson <[email protected]>
  • Loading branch information
3 people authored Oct 8, 2021
1 parent 50f0473 commit 606bd27
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 12 deletions.
33 changes: 33 additions & 0 deletions src/RpcClient/Models/RpcFoundStates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Linq;
using Neo.IO.Json;

namespace Neo.Network.RPC.Models
{
public class RpcFoundStates
{
public bool Truncated;
public (byte[] key, byte[] value)[] Results;
public byte[] FirstProof;
public byte[] LastProof;

public static RpcFoundStates FromJson(JObject json)
{
return new RpcFoundStates
{
Truncated = json["truncated"].AsBoolean(),
Results = ((JArray)json["results"])
.Select(j => (
Convert.FromBase64String(j["key"].AsString()),
Convert.FromBase64String(j["value"].AsString())
))
.ToArray(),
FirstProof = ProofFromJson(json["firstProof"]),
LastProof = ProofFromJson(json["lastProof"]),
};
}

static byte[] ProofFromJson(JObject json)
=> json == null ? null : Convert.FromBase64String(json.AsString());
}
}
25 changes: 25 additions & 0 deletions src/RpcClient/Models/RpcStateRoot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Linq;
using Neo.IO.Json;
using Neo.Network.P2P.Payloads;

namespace Neo.Network.RPC.Models
{
public class RpcStateRoot
{
public byte Version;
public uint Index;
public UInt256 RootHash;
public Witness Witness;

public static RpcStateRoot FromJson(JObject json)
{
return new RpcStateRoot
{
Version = (byte)json["version"].AsNumber(),
Index = (uint)json["index"].AsNumber(),
RootHash = UInt256.Parse(json["roothash"].AsString()),
Witness = ((JArray)json["witnesses"]).Select(p => Utility.WitnessFromJson(p)).FirstOrDefault()
};
}
}
}
60 changes: 48 additions & 12 deletions src/RpcClient/RpcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Neo.SmartContract.Manifest;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -66,13 +67,18 @@ public void Dispose()
}
#endregion

public async Task<RpcResponse> SendAsync(RpcRequest request)
static RpcRequest AsRpcRequest(string method, params JObject[] paraArgs)
{
return new RpcRequest
{
Id = 1,
JsonRpc = "2.0",
Method = method,
Params = paraArgs
};
}
static RpcResponse AsRpcResponse(string content)
{
if (disposedValue) throw new ObjectDisposedException(nameof(RpcClient));

var requestJson = request.ToJson().ToString();
using var result = await httpClient.PostAsync(baseAddress, new StringContent(requestJson, Encoding.UTF8)).ConfigureAwait(false);
var content = await result.Content.ReadAsStringAsync();
var response = RpcResponse.FromJson(JObject.Parse(content));
response.RawResponse = content;

Expand All @@ -84,16 +90,46 @@ public async Task<RpcResponse> SendAsync(RpcRequest request)
return response;
}

public virtual async Task<JObject> RpcSendAsync(string method, params JObject[] paraArgs)
HttpRequestMessage AsHttpRequest(RpcRequest request)
{
var request = new RpcRequest
var requestJson = request.ToJson().ToString();
return new HttpRequestMessage(HttpMethod.Post, baseAddress)
{
Id = 1,
JsonRpc = "2.0",
Method = method,
Params = paraArgs
Content = new StringContent(requestJson, Neo.Utility.StrictUTF8)
};
}

public RpcResponse Send(RpcRequest request)
{
if (disposedValue) throw new ObjectDisposedException(nameof(RpcClient));

using var requestMsg = AsHttpRequest(request);
using var responseMsg = httpClient.Send(requestMsg);
using var contentStream = responseMsg.Content.ReadAsStream();
using var contentReader = new StreamReader(contentStream);
return AsRpcResponse(contentReader.ReadToEnd());
}

public async Task<RpcResponse> SendAsync(RpcRequest request)
{
if (disposedValue) throw new ObjectDisposedException(nameof(RpcClient));

using var requestMsg = AsHttpRequest(request);
using var responseMsg = await httpClient.SendAsync(requestMsg).ConfigureAwait(false);
var content = await responseMsg.Content.ReadAsStringAsync();
return AsRpcResponse(content);
}

public virtual JObject RpcSend(string method, params JObject[] paraArgs)
{
var request = AsRpcRequest(method, paraArgs);
var response = Send(request);
return response.Result;
}

public virtual async Task<JObject> RpcSendAsync(string method, params JObject[] paraArgs)
{
var request = AsRpcRequest(method, paraArgs);
var response = await SendAsync(request).ConfigureAwait(false);
return response.Result;
}
Expand Down
85 changes: 85 additions & 0 deletions src/RpcClient/StateAPI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Neo.IO.Json;
using Neo.Network.RPC.Models;

namespace Neo.Network.RPC
{
public class StateAPI
{
private readonly RpcClient rpcClient;

public StateAPI(RpcClient rpc)
{
this.rpcClient = rpc;
}

public async Task<RpcStateRoot> GetStateRootAsync(uint index)
{
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName(), index).ConfigureAwait(false);
return RpcStateRoot.FromJson(result);
}

public async Task<byte[]> GetProofAsync(UInt256 rootHash, UInt160 scriptHash, byte[] key)
{
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName(),
rootHash.ToString(), scriptHash.ToString(), Convert.ToBase64String(key)).ConfigureAwait(false);
return Convert.FromBase64String(result.AsString());
}

public async Task<byte[]> VerifyProofAsync(UInt256 rootHash, byte[] proofBytes)
{
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName(),
rootHash.ToString(), Convert.ToBase64String(proofBytes)).ConfigureAwait(false);

return Convert.FromBase64String(result.AsString());
}

public async Task<(uint? localRootIndex, uint? validatedRootIndex)> GetStateHeightAsync()
{
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName()).ConfigureAwait(false);
var localRootIndex = ToNullableUint(result["localrootindex"]);
var validatedRootIndex = ToNullableUint(result["validatedrootindex"]);
return (localRootIndex, validatedRootIndex);
}

static uint? ToNullableUint(JObject json) => (json == null) ? (uint?)null : (uint?)json.AsNumber();

public static JObject[] MakeFindStatesParams(UInt256 rootHash, UInt160 scriptHash, ReadOnlySpan<byte> prefix, ReadOnlySpan<byte> from = default, int? count = null)
{
var paramCount = from.Length == 0 ? 3 : count == null ? 4 : 5;
var @params = new JObject[paramCount];
@params[0] = rootHash.ToString();
@params[1] = scriptHash.ToString();
@params[2] = Convert.ToBase64String(prefix);
if (from.Length > 0)
{
@params[3] = Convert.ToBase64String(from);
if (count.HasValue)
{
@params[4] = count.Value;
}
}
return @params;
}

public async Task<RpcFoundStates> FindStatesAsync(UInt256 rootHash, UInt160 scriptHash, ReadOnlyMemory<byte> prefix, ReadOnlyMemory<byte> from = default, int? count = null)
{
var @params = MakeFindStatesParams(rootHash, scriptHash, prefix.Span, from.Span, count);
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName(), @params).ConfigureAwait(false);

return RpcFoundStates.FromJson(result);
}

public async Task<byte[]> GetStateAsync(UInt256 rootHash, UInt160 scriptHash, byte[] key)
{
var result = await rpcClient.RpcSendAsync(RpcClient.GetRpcName(),
rootHash.ToString(), scriptHash.ToString(), Convert.ToBase64String(key)).ConfigureAwait(false);
return Convert.FromBase64String(result.AsString());
}
}
}

0 comments on commit 606bd27

Please sign in to comment.