Skip to content

Commit

Permalink
feat: implement functionality to make rpc call to other nodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
Zurcusa committed Aug 27, 2024
1 parent 8663462 commit ce6d952
Show file tree
Hide file tree
Showing 19 changed files with 275 additions and 16 deletions.
18 changes: 14 additions & 4 deletions src/main/java/com/limechain/config/HostConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.limechain.utils.DivLogger;
import lombok.Getter;

import java.util.List;
import java.util.logging.Level;

/**
Expand All @@ -19,10 +20,10 @@ public class HostConfig {
// private final NodeRole nodeRole;
private final String rpcNodeAddress;

private String polkadotGenesisPath = "genesis/polkadot.json";
private String kusamaGenesisPath = "genesis/ksmcc3.json";
private String westendGenesisPath = "genesis/westend2.json";
private String localGenesisPath = "genesis/westend-local.json";
private final String polkadotGenesisPath = "genesis/polkadot.json";
private final String kusamaGenesisPath = "genesis/ksmcc3.json";
private final String westendGenesisPath = "genesis/westend2.json";
private final String localGenesisPath = "genesis/westend-local.json";

private static final DivLogger log = new DivLogger();

Expand Down Expand Up @@ -66,4 +67,13 @@ public String getGenesisPath() {
case LOCAL -> localGenesisPath;
};
}

public List<String> getHttpsRpcEndpoints() {
return switch (chain) {
case POLKADOT -> RpcConstants.POLKADOT_HTTPS_RPC;
case KUSAMA -> RpcConstants.KUSAMA_HTTPS_RPC;
case WESTEND -> RpcConstants.WESTEND_HTTPS_RPC;
case LOCAL -> List.of();
};
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/limechain/constants/RpcConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,27 @@
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.util.List;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class RpcConstants {
public static final String POLKADOT_WS_RPC = "wss://rpc.polkadot.io";
public static final String KUSAMA_WS_RPC = "wss://kusama-rpc.polkadot.io";
public static final String WESTEND_WS_RPC = "wss://westend-rpc.polkadot.io";

public static final List<String> POLKADOT_HTTPS_RPC = List.of(
"https://rpc.ibp.network/polkadot",
"https://polkadot-rpc.dwellir.com",
"https://rpc.polkadot.io"
);
public static final List<String> KUSAMA_HTTPS_RPC = List.of(
"https://rpc.ibp.network/kusama",
"https://kusama-rpc.dwellir.com",
"https://kusama-rpc.polkadot.io"
);
public static final List<String> WESTEND_HTTPS_RPC = List.of(
"https://rpc.ibp.network/westend",
"https://westend-rpc.dwellir.com",
"https://westend-rpc.polkadot.io"
);
}
27 changes: 27 additions & 0 deletions src/main/java/com/limechain/rpc/BlockRpcClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.limechain.rpc;

import com.limechain.polkaj.Hash256;
import com.limechain.rpc.dto.ChainGetHeaderResult;
import com.limechain.rpc.dto.GrandpaRoundStateResult;
import com.limechain.rpc.dto.RpcMethod;
import com.limechain.rpc.dto.RpcResponse;

import java.util.List;

public final class BlockRpcClient 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);
}

public static GrandpaRoundStateResult getGrandpaRoundState() {
RpcResponse response = sendRpcRequest(RpcMethod.GRANDPA_ROUND_STATE, List.of());
return getResult(response, GrandpaRoundStateResult.class);
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/limechain/rpc/LoadBalancer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.limechain.rpc;

import com.limechain.config.HostConfig;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

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);
}
}
45 changes: 45 additions & 0 deletions src/main/java/com/limechain/rpc/RpcClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
public sealed class RpcClient permits BlockRpcClient {

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);

private static String createRpcRequestJson(String method, List<Object> params) {
RpcRequest request = new RpcRequest(ID_COUNTER.getAndAdd(1), method, params);
return JsonUtil.stringify(request);
}

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);
}

protected static <T> T getResult(RpcResponse response, Class<T> type) {
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()), type);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/limechain/rpc/dto/ChainGetHeaderResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

@Data
@Reflectable
public class ChainGetHeaderResult {

private String parentHash;
private String number;
private String stateRoot;
private String extrinsicsRoot;
private Digest digest;
}
13 changes: 13 additions & 0 deletions src/main/java/com/limechain/rpc/dto/Digest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

import java.util.List;

@Data
@Reflectable
public class Digest {

private List<String> logs;
}
15 changes: 15 additions & 0 deletions src/main/java/com/limechain/rpc/dto/GrandpaRoundStateResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

import java.util.List;

@Data
@Reflectable
public class GrandpaRoundStateResult {

private int setId;
private RoundState best;
private List<RoundState> background;
}
14 changes: 14 additions & 0 deletions src/main/java/com/limechain/rpc/dto/RoundState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

@Data
@Reflectable
public class RoundState {
private int round;
private int totalWeight;
private int thresholdWeight;
private Votes prevotes;
private Votes precommits;
}
13 changes: 13 additions & 0 deletions src/main/java/com/limechain/rpc/dto/RpcError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

@Data
@Reflectable
public class RpcError {

private int code;
private String message;
private String data;
}
17 changes: 17 additions & 0 deletions src/main/java/com/limechain/rpc/dto/RpcMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.limechain.rpc.dto;

import lombok.Getter;

@Getter
public enum RpcMethod {

CHAIN_GET_FINALIZED_HEAD("chain_getFinalizedHead"),
CHAIN_GET_HEADER("chain_getHeader"),
GRANDPA_ROUND_STATE("grandpa_roundState");

RpcMethod(String method) {
this.method = method;
}

private final String method;
}
20 changes: 20 additions & 0 deletions src/main/java/com/limechain/rpc/dto/RpcRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Reflectable
@NoArgsConstructor
@AllArgsConstructor
public class RpcRequest {

private int id;
private final String jsonrpc = "2.0";
private String method;
private List<Object> params;
}
16 changes: 16 additions & 0 deletions src/main/java/com/limechain/rpc/dto/RpcResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Reflectable
@NoArgsConstructor
public class RpcResponse {

private int id;
private final String jsonrpc = "2.0";
private Object result;
private RpcError error;
}
14 changes: 14 additions & 0 deletions src/main/java/com/limechain/rpc/dto/Votes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.limechain.rpc.dto;

import com.limechain.teavm.annotation.Reflectable;
import lombok.Data;

import java.util.List;

@Data
@Reflectable
public class Votes {

private int currentWeight;
private List<String> missing;
}
3 changes: 1 addition & 2 deletions src/main/java/com/limechain/rpc/server/CommonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
public class CommonConfig {

private static Map<Class<?>, Object> beans = new HashMap<>();
private static final Map<Class<?>, Object> beans = new HashMap<>();

public static void start() {
getBean(SystemInfo.class);
Expand Down Expand Up @@ -95,5 +95,4 @@ private static WarpSyncMachine warpSyncMachine(Network network, ChainService cha
WarpSyncState warpSyncState) {
return new WarpSyncMachine(network, chainService, syncState, warpSyncState);
}

}
1 change: 0 additions & 1 deletion src/main/java/com/limechain/storage/block/SyncState.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import lombok.extern.java.Log;

import java.math.BigInteger;
import java.util.Arrays;

@Getter
@Log
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/limechain/teavm/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private static void asyncHttpRequest(String method, String url, String body, Asy
}

@JSBody(params = {"method", "url", "body", "callback"}, script = "return asyncHttpRequest(method, url, body, callback);")
public static native void createAsyncHttpRequest(String method, String url, String body, HttpRequestCallback callback);
private static native void createAsyncHttpRequest(String method, String url, String body, HttpRequestCallback callback);

@JSFunctor
private interface HttpRequestCallback extends JSObject {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/limechain/utils/json/JsonUtil.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.limechain.utils.json;

import com.limechain.teavm.HttpRequest;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtil {

// Prevents instantiation
private JsonUtil() {}

public static Object parseJson(String jsonString) {
return new JsonParser(jsonString).parse();
}
Expand Down
Loading

0 comments on commit ce6d952

Please sign in to comment.