Skip to content

Commit

Permalink
feat: fix creation of chainspec form json
Browse files Browse the repository at this point in the history
  • Loading branch information
Zurcusa committed Aug 7, 2024
1 parent c2c4fc7 commit 7b13bfb
Show file tree
Hide file tree
Showing 18 changed files with 153 additions and 271 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/update-light-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
rpc_answer.json
- run: | # Overwrite the `lightSyncState` field of the existing chain spec with the value of spec that's been downloaded.
tmp=$(mktemp)
output=./repo/genesis/${{ steps.get-chain-id.outputs.id }}.json
output=./repo/src/main/webapp/genesis/${{ steps.get-chain-id.outputs.id }}.json
jq --slurpfile downloaded ./chain_spec.json '.lightSyncState = $downloaded[0].lightSyncState' "$output" > "$tmp"
mv "$tmp" "$output"
- uses: actions/upload-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ out/
/test

### Local Chain Spec
/genesis/westend-local.json
/src/main/webapp/genesis/westend-local.json
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ RUN apt-get update && \

WORKDIR /usr/app/

COPY genesis /usr/app/genesis
COPY src/main/webapp/genesis /usr/app/genesis
COPY --from=build_image /usr/build/build/libs/*.jar /usr/app/app.jar

ENTRYPOINT ["java","-jar","/usr/app/app.jar"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Now you have 2 options:
work. Without it, the light client won't have a checkpoint to start from
and could be long-range attacked

2. Create a new `westend-local.json` inside of the `genesis` project directory.
2. Create a new `westend-local.json` inside of the `src/main/webapp/genesis` project directory.
3. Copy the contents of the `result` field from the fetched chain spec into the newly created `westend-local.json`.
4. In order to comply with the project requirements change the json structured as follows:

Expand Down
194 changes: 0 additions & 194 deletions genesis/westend-raw.json

This file was deleted.

2 changes: 1 addition & 1 deletion local_dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ boot_nodes=$(echo "$boot_nodes_resp" | jq '.result')
# Paste boot nodes
final_json=$(echo "$output_json" | jq --argjson bootNodes "$boot_nodes" '.bootNodes = $bootNodes')

echo "$final_json" > "./genesis/westend-local.json"
echo "$final_json" > "./src/main/webapp/genesis/westend-local.json"
2 changes: 2 additions & 0 deletions src/main/java/com/limechain/chain/spec/ChainSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.limechain.utils.json.ObjectMapper;
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;
import java.io.Serializable;
Expand All @@ -11,6 +12,7 @@
* Contains the chain spec data, deserialized and parsed in-memory into appropriate structures
*/
@Getter
@Setter
public class ChainSpec implements Serializable {
private String id;
private String name;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/limechain/config/HostConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public class HostConfig {
private final String rpcNodeAddress;

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

private static final DivLogger log = new DivLogger();

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/limechain/teavm/HttpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.limechain.teavm;

import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;

public class HttpRequest {
@JSBody(params = {"method", "url", "body"}, script = "return httpRequestSync(method, url, body);")
public static native String httpRequestSync(String method, String url, JSObject body);
}
22 changes: 5 additions & 17 deletions src/main/java/com/limechain/utils/json/JsonUtil.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
package com.limechain.utils.json;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import com.limechain.teavm.HttpRequest;

import java.util.Map;

public class JsonUtil {

static Map<String, Object> parseJson(String jsonPath) {
try {
return new JsonParser(readJsonFromFile(jsonPath)).parse();
} catch (IOException e) {
throw new IllegalStateException("Failed to parse json at path " + jsonPath);
}
return new JsonParser(readJsonFromFile(jsonPath)).parse();
}

private static String readJsonFromFile(String filePath) throws IOException {
StringBuilder jsonStringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
jsonStringBuilder.append(line);
}
}
return jsonStringBuilder.toString();
private static String readJsonFromFile(String filePath) {
return HttpRequest.httpRequestSync("GET", filePath, null);
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/limechain/utils/json/ObjectFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.limechain.utils.json;

import com.limechain.chain.spec.ChainSpec;

public class ObjectFactory {
public static ChainSpec createChainSpec() {
return new ChainSpec();
}
}
135 changes: 83 additions & 52 deletions src/main/java/com/limechain/utils/json/ObjectMapper.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.limechain.utils.json;

import lombok.extern.java.Log;
import com.limechain.chain.spec.ChainSpec;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Log
public class ObjectMapper {

boolean failOnUnknown;
Expand All @@ -20,65 +18,98 @@ public ObjectMapper(boolean failOnUnknown) {
public <T> T mapToClass(String jsonPath, Class<T> clazz) throws IOException {
Map<String, Object> jsonMap = JsonUtil.parseJson(jsonPath);

try {
T instance = clazz.getDeclaredConstructor().newInstance();
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Field field = findField(clazz, key);
if (field != null) {
field.setAccessible(true);
field.set(instance, convertValue(field.getType(), value));
}
}
return instance;
} catch (Exception e) {
throw new IOException("Failed to map JSON to class", e);
}
T instance = createInstance(clazz);
populateFields(instance, jsonMap);

return instance;
}

private Field findField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
if (failOnUnknown) {
throw new NoSuchFieldException("Field " + fieldName + " does not exist in " + clazz.getName());
} else {
log.fine("Field " + fieldName + " does not exist in " + clazz.getName());
return null;
}
private <T> T createInstance(Class<T> clazz) {
if (clazz == ChainSpec.class) {
return (T) ObjectFactory.createChainSpec();
}
// Handle other types similarly
throw new IllegalArgumentException("Unsupported class type: " + clazz.getName());
}

private static Object convertValue(Class<?> type, Object value) {
if (value == null) {
return null;
private void populateFields(Object instance, Map<String, Object> jsonMap) throws IOException {
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
setField(instance, key, value);
}
}

if (type.isInstance(value)) {
return value;
} else if (type == Integer.class || type == int.class) {
return ((Number) value).intValue();
} else if (type == Long.class || type == long.class) {
return ((Number) value).longValue();
} else if (type == Double.class || type == double.class) {
return ((Number) value).doubleValue();
} else if (type == Boolean.class || type == boolean.class) {
return value;
} else if (type == String.class) {
return value.toString();
} else if (type.isArray()) {
return convertArray(type.getComponentType(), (List<?>) value);
private void setField(Object instance, String key, Object value) throws IOException {
if (instance instanceof ChainSpec) {
setChainSpecFields((ChainSpec) instance, key, value);
} else {
throw new IOException("Unsupported object type for field setting.");
}
}

throw new RuntimeException("Unsupported field type: " + type);
private void setChainSpecFields(ChainSpec instance, String key, Object value) throws IOException {
switch (key) {
case "id":
instance.setId(convertValue(String.class, value));
break;
case "name":
instance.setName(convertValue(String.class, value));
break;
case "protocolId":
instance.setProtocolId(convertValue(String.class, value));
break;
case "bootNodes":
instance.setBootNodes(convertValue(String[].class, value));
break;
case "lightSyncState":
instance.setLightSyncState(convertValue(Map.class, value));
break;
default: {
if (failOnUnknown) {
throw new IOException("Unsupported field key: " + key);
}
}
}
}

private static Object convertArray(Class<?> componentType, List<?> jsonArray) {
Object array = Array.newInstance(componentType, jsonArray.size());
for (int i = 0; i < jsonArray.size(); i++) {
Array.set(array, i, convertValue(componentType, jsonArray.get(i)));
private <T> T convertValue(Class<T> type, Object value) {
if (value == null) {
return null;
}
if (type == String.class) {
return (T) value.toString();
} else if (type == int.class || type == Integer.class) {
return (T) Integer.valueOf(value.toString());
} else if (type == long.class || type == Long.class) {
return (T) Long.valueOf(value.toString());
} else if (type == double.class || type == Double.class) {
return (T) Double.valueOf(value.toString());
} else if (type == float.class || type == Float.class) {
return (T) Float.valueOf(value.toString());
} else if (type == boolean.class || type == Boolean.class) {
return (T) Boolean.valueOf(value.toString());
} else if (type == char.class || type == Character.class) {
return (T) Character.valueOf(value.toString().charAt(0));
} else if (type.isArray()) {
// Handle arrays
Class<?> componentType = type.getComponentType();
List<?> list = (List<?>) value;
Object array = java.lang.reflect.Array.newInstance(componentType, list.size());
for (int i = 0; i < list.size(); i++) {
java.lang.reflect.Array.set(array, i, convertValue(componentType, list.get(i)));
}
return (T) array;
} else if (type == Map.class) {
// Handle maps
Map<String, String> map = (Map<String, String>) value;
Map<String, String> resultMap = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
resultMap.put(entry.getKey(), convertValue(String.class, entry.getValue()));
}
return (T) resultMap;
} else {
throw new IllegalArgumentException("Unsupported conversion type: " + type.getName());
}
return array;
}
}
21 changes: 21 additions & 0 deletions src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2014 Alexey Andreev.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<script src="https://unpkg.com/libp2p/dist/index.min.js"></script>
<script src=""></script>
<script type="text/javascript" charset="utf-8" src="js/fruzhin.js"></script>
<script type="text/javascript" charset="utf-8" src="js/http.js"></script>
</head>
<body onload="main()">
<script>
Expand Down
15 changes: 15 additions & 0 deletions src/main/webapp/js/http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function httpRequestSync(method, url, body) {
var xhr = new XMLHttpRequest();
xhr.open(method, url, false); // false for synchronous request
xhr.setRequestHeader('Content-Type', 'application/json');
if (method === 'POST' && body) {
xhr.send(JSON.stringify(body));
} else {
xhr.send();
}
if (xhr.status === 200) {
return xhr.responseText;
} else {
throw new Error('Request failed with status ' + xhr.status);
}
}

0 comments on commit 7b13bfb

Please sign in to comment.