forked from LimeChain/Fruzhin
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement functionality to make rpc call to other nodes. (#7)
- What does this PR do?\ Implements functionality to make RPC calls to other nodes. - Why are these changes needed? So that Fruzhin fallbacks to rpc call when populating sync state if protocols fail. Fixes LimeChain#494 ## Checklist: - [X] I have read the [contributing guidelines](https://github.com/LimeChain/Fruzhin/blob/dev/CONTRIBUTING.md). - [X] My PR title matches the [Conventional Commits spec](https://www.conventionalcommits.org/). - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [X] All new and existing tests passed. --------- Co-authored-by: Мurаd Hаmzа <[email protected]>
- Loading branch information
Showing
32 changed files
with
552 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.limechain.rpc; | ||
|
||
import com.limechain.polkaj.Hash256; | ||
import com.limechain.rpc.dto.ChainGetHeaderResult; | ||
import com.limechain.rpc.dto.RpcMethod; | ||
import com.limechain.rpc.dto.RpcResponse; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* An implementation of {@link RpcClient}, which implements RPC calls from the "chain" category. | ||
*/ | ||
public final class ChainRpcClient extends RpcClient { | ||
|
||
public static Hash256 getLastFinalizedBlockHash() { | ||
RpcResponse response = sendRpcRequest(RpcMethod.CHAIN_GET_FINALIZED_HEAD, List.of()); | ||
return Hash256.from(getResult(response, String.class)); | ||
} | ||
|
||
public static ChainGetHeaderResult getHeader(String blockHash) { | ||
RpcResponse response = sendRpcRequest(RpcMethod.CHAIN_GET_HEADER, List.of(blockHash)); | ||
return getResult(response, ChainGetHeaderResult.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.limechain.rpc; | ||
|
||
import org.teavm.jso.JSObject; | ||
|
||
/** | ||
* A functional interface used to export rpc functionalities to the user. A function which conforms with "sendRequest" | ||
* signature can be exported via {@link com.limechain.Main}{@code .exportAPI(Function, JSString)} | ||
*/ | ||
@FunctionalInterface | ||
public interface Function extends JSObject { | ||
|
||
String sendRequest(String method, String[] params); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.limechain.rpc; | ||
|
||
import com.limechain.rpc.dto.GrandpaRoundStateResult; | ||
import com.limechain.rpc.dto.RpcMethod; | ||
import com.limechain.rpc.dto.RpcResponse; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* An implementation of {@link RpcClient}, which implements RPC calls from the "grandpa" category. | ||
*/ | ||
public final class GrandpaRpcClient extends RpcClient { | ||
|
||
public static GrandpaRoundStateResult getGrandpaRoundState() { | ||
RpcResponse response = sendRpcRequest(RpcMethod.GRANDPA_ROUND_STATE, List.of()); | ||
return getResult(response, GrandpaRoundStateResult.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.limechain.rpc; | ||
|
||
import com.limechain.config.HostConfig; | ||
|
||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* A simple load balancer that switches between provided endpoints. Each consecutive call takes the latter endpoint | ||
* in the provided list. | ||
*/ | ||
public class LoadBalancer { | ||
|
||
private final List<String> endpoints; | ||
private final AtomicInteger index; | ||
|
||
public LoadBalancer(HostConfig hostConfig) { | ||
this.endpoints = hostConfig.getHttpsRpcEndpoints(); | ||
this.index = new AtomicInteger(0); | ||
} | ||
|
||
public String getNextEndpoint() { | ||
int currentIndex = index.getAndUpdate(i -> (i + 1) % endpoints.size()); | ||
return endpoints.get(currentIndex); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.limechain.rpc; | ||
|
||
import com.limechain.config.HostConfig; | ||
import com.limechain.rpc.dto.RpcMethod; | ||
import com.limechain.rpc.dto.RpcRequest; | ||
import com.limechain.rpc.dto.RpcResponse; | ||
import com.limechain.rpc.server.AppBean; | ||
import com.limechain.teavm.HttpRequest; | ||
import com.limechain.utils.json.JsonUtil; | ||
import com.limechain.utils.json.ObjectMapper; | ||
import lombok.AccessLevel; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* Base class for executing RPC requests. | ||
*/ | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public sealed class RpcClient permits ChainRpcClient, GrandpaRpcClient { | ||
|
||
private static final String POST = "POST"; | ||
private static final AtomicInteger ID_COUNTER = new AtomicInteger(1); | ||
private static final LoadBalancer LOAD_BALANCER = new LoadBalancer(AppBean.getBean(HostConfig.class)); | ||
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(false); | ||
|
||
/** | ||
* Send an RPC request. Currently used only by the exported RPC client. | ||
* | ||
* @param method {@link String} representation of the RPC method name. For example "system_name". | ||
* @param params An array of parameters for the sent RPC request. | ||
* @return The {@link String} representation of the received RPC json result. | ||
*/ | ||
public static String sendRpcRequest(String method, Object[] params) { | ||
return HttpRequest.createHttpRequest(POST, LOAD_BALANCER.getNextEndpoint(), | ||
createRpcRequestJson(method, List.of(params))); | ||
} | ||
|
||
/** | ||
* Send an RPC request. Used by the specific implementations of the RpcClient. | ||
* | ||
* @param method Enum representation of an RPC method name. | ||
* @param params An array of parameters for the sent RPC request. | ||
* @return The {@link RpcResponse} representation of the received RPC json result. | ||
*/ | ||
protected static RpcResponse sendRpcRequest(RpcMethod method, List<Object> params) { | ||
String jsonResult = HttpRequest.asyncHttpRequest(POST, LOAD_BALANCER.getNextEndpoint(), | ||
createRpcRequestJson(method.getMethod(), params)); | ||
return OBJECT_MAPPER.mapToClass(jsonResult, RpcResponse.class); | ||
} | ||
|
||
private static String createRpcRequestJson(String method, List<Object> params) { | ||
RpcRequest request = new RpcRequest(ID_COUNTER.getAndAdd(1), method, params); | ||
return JsonUtil.stringify(request); | ||
} | ||
|
||
/** | ||
* Method used to map an {@link RpcResponse} result to a provided class type. This is needed because TeaVM does not | ||
* support use of {@link java.lang.reflect.ParameterizedType} and we cannot use an object mapper with generics | ||
* inside. | ||
* | ||
* @param response the {@link RpcResponse} whose result we have to map to an object. | ||
* @param klazz the desired class for the mapping. | ||
* @return a mapped version of the response result in the form of the provided {@code klazz} type. | ||
*/ | ||
protected static <T> T getResult(RpcResponse response, Class<T> klazz) { | ||
if (response.getError() != null) { | ||
throw new IllegalStateException("RPC request resulted in an error with code:" + response.getError().getCode() | ||
+ " and message:" + response.getError().getMessage()); | ||
} | ||
|
||
return OBJECT_MAPPER.mapToClass(JsonUtil.stringify(response.getResult()), klazz); | ||
} | ||
} |
Oops, something went wrong.