+// implements XdagJsonRpcRequestVisitor {
+// private static final Logger LOGGER = LoggerFactory.getLogger(XdagJsonRpcHandler.class);
+//
+//// private final EthSubscriptionNotificationEmitter emitter;
+// private final JsonRpcSerializer serializer;
+//
+// public XdagJsonRpcHandler(JsonRpcSerializer serializer) {
+// this.serializer = serializer;
+// }
+//// public XdagJsonRpcHandler(EthSubscriptionNotificationEmitter emitter, JsonRpcSerializer serializer) {
+//// this.emitter = emitter;
+//// this.serializer = serializer;
+//// }
+//
+// @Override
+// protected void channelRead0(ChannelHandlerContext ctx, ByteBufHolder msg) {
+// try {
+// XdagJsonRpcRequest request = serializer.deserializeRequest(
+// new ByteBufInputStream(msg.copy().content())
+// );
+//
+// // TODO(mc) we should support the ModuleDescription method filters
+// JsonRpcResultOrError resultOrError = request.accept(this, ctx);
+// JsonRpcIdentifiableMessage response = resultOrError.responseFor(request.getId());
+// ctx.writeAndFlush(new TextWebSocketFrame(serializer.serializeMessage(response)));
+// return;
+// } catch (IOException e) {
+// LOGGER.trace("Not a known or valid JsonRpcRequest", e);
+// }
+//
+// // delegate to the next handler if the message can't be matched to a known JSON-RPC request
+// ctx.fireChannelRead(msg.retain());
+// }
+//
+// @Override
+// public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+//// emitter.unsubscribe(ctx.channel());
+// super.channelInactive(ctx);
+// }
+//
+// @Override
+// public JsonRpcResultOrError visit(EthUnsubscribeRequest request, ChannelHandlerContext ctx) {
+//// boolean unsubscribed = emitter.unsubscribe(request.getParams().getSubscriptionId());
+//// return new JsonRpcBooleanResult(unsubscribed);
+// return null;
+// }
+//
+// @Override
+// public JsonRpcResultOrError visit(EthSubscribeRequest request, ChannelHandlerContext ctx) {
+//// return request.getParams().accept(emitter, ctx.channel());
+// return null;
+// }
+//}
diff --git a/src/main/java/io/xdag/rpc/serialize/JacksonBasedRpcSerializer.java b/src/main/java/io/xdag/rpc/serialize/JacksonBasedRpcSerializer.java
new file mode 100644
index 00000000..5a352929
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/serialize/JacksonBasedRpcSerializer.java
@@ -0,0 +1,25 @@
+package io.xdag.rpc.serialize;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.xdag.rpc.jsonrpc.JsonRpcMessage;
+import io.xdag.rpc.modules.XdagJsonRpcRequest;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class JacksonBasedRpcSerializer implements JsonRpcSerializer {
+ //From https://fasterxml.github.io/jackson-databind/javadoc/2.5/com/fasterxml/jackson/databind/ObjectMapper.html
+ // ObjectMapper is thread-safe as long as the config methods are not called after the serialiation begins.
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public String serializeMessage(JsonRpcMessage message) throws JsonProcessingException {
+ return mapper.writeValueAsString(message);
+ }
+
+ @Override
+ public XdagJsonRpcRequest deserializeRequest(InputStream source) throws IOException {
+ return mapper.readValue(source, XdagJsonRpcRequest.class);
+ }
+}
diff --git a/src/main/java/io/xdag/rpc/serialize/JsonRpcSerializer.java b/src/main/java/io/xdag/rpc/serialize/JsonRpcSerializer.java
new file mode 100644
index 00000000..322d902b
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/serialize/JsonRpcSerializer.java
@@ -0,0 +1,23 @@
+package io.xdag.rpc.serialize;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import io.xdag.rpc.jsonrpc.JsonRpcMessage;
+import io.xdag.rpc.modules.XdagJsonRpcRequest;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface JsonRpcSerializer {
+ /**
+ * @return a JsonRpcMessage serialized into a JSON string
+ * @throws JsonProcessingException when serialization fails
+ */
+ String serializeMessage(JsonRpcMessage message) throws JsonProcessingException;
+
+ /**
+ * @return an RskJsonRpcRequest deserialized from a JSON string in the source stream
+ * @throws IOException when deserialization fails
+ */
+ XdagJsonRpcRequest deserializeRequest(InputStream source) throws IOException;
+}
+
diff --git a/src/main/java/io/xdag/rpc/utils/HttpUtils.java b/src/main/java/io/xdag/rpc/utils/HttpUtils.java
new file mode 100644
index 00000000..ba424275
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/utils/HttpUtils.java
@@ -0,0 +1,24 @@
+package io.xdag.rpc.utils;
+
+public class HttpUtils {
+ /** This function is strongly based on the function from netty 4.1, since
+ * we have an older version from it and do not need the overhead of checking
+ * the rest of the implementation for security reasons.
+ *
+ * @param contentTypeValue the value obtained from the header Content-Type
+ * @return only the mime type, ignoring charset or other values after ;
+ */
+ public static String getMimeType(String contentTypeValue) {
+ if (contentTypeValue == null) {
+ return null;
+ }
+
+ int indexOfSemicolon = contentTypeValue.indexOf(';');
+ if (indexOfSemicolon != -1) {
+ return contentTypeValue.substring(0, indexOfSemicolon);
+ } else {
+ return contentTypeValue.length() > 0 ? contentTypeValue : null;
+ }
+
+ }
+}
diff --git a/src/main/java/io/xdag/rpc/utils/TypeConverter.java b/src/main/java/io/xdag/rpc/utils/TypeConverter.java
new file mode 100644
index 00000000..ab4b21ec
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/utils/TypeConverter.java
@@ -0,0 +1,132 @@
+package io.xdag.rpc.utils;
+
+import org.bouncycastle.util.encoders.Hex;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class TypeConverter {
+ private static final Pattern LEADING_ZEROS_PATTERN = Pattern.compile("0x(0)+");
+
+ private TypeConverter() {
+ throw new IllegalAccessError("Utility class");
+ }
+
+ public static BigInteger stringNumberAsBigInt(String input) {
+ if (input.startsWith("0x")) {
+ return TypeConverter.stringHexToBigInteger(input);
+ } else {
+ return TypeConverter.stringDecimalToBigInteger(input);
+ }
+ }
+
+ public static BigInteger stringHexToBigInteger(String input) {
+ if(!input.startsWith("0x")) {
+ throw new NumberFormatException("Invalid hex number, expected 0x prefix");
+ }
+ String hexa = input.substring(2);
+ return new BigInteger(hexa, 16);
+ }
+
+ private static BigInteger stringDecimalToBigInteger(String input) {
+ return new BigInteger(input);
+ }
+
+ public static byte[] stringHexToByteArray(String x) {
+ String result = x;
+ if (x.startsWith("0x")) {
+ result = x.substring(2);
+ }
+ if (result.length() % 2 != 0) {
+ result = "0" + result;
+ }
+ return Hex.decode(result);
+ }
+
+ public static byte[] stringToByteArray(String input) {
+ return input.getBytes(StandardCharsets.UTF_8);
+ }
+
+ public static String toJsonHex(byte[] x) {
+ String result = toUnformattedJsonHex(x);
+
+ if ("0x".equals(result)) {
+ return "0x00";
+ }
+
+ return result;
+ }
+
+// public static String toJsonHex(Coin x) {
+// return x != null ? x.asBigInteger().toString() : "" ;
+// }
+
+ public static String toJsonHex(String x) {
+ return "0x"+x;
+ }
+
+ /**
+ * @return A Hex representation of n WITHOUT leading zeroes
+ */
+ public static String toQuantityJsonHex(long n) {
+ return "0x" + Long.toHexString(n);
+ }
+
+ public static String toQuantityJsonHex(double n) {
+ return "" + n;
+ }
+
+ /**
+ * @return A Hex representation of n WITHOUT leading zeroes
+ */
+ public static String toQuantityJsonHex(BigInteger n) {
+ return "0x"+ n.toString(16);
+ }
+
+ /**
+ * Converts a byte array to a string according to ethereum json-rpc specifications, null and empty
+ * convert to 0x.
+ *
+ * @param x An unformatted byte array
+ * @return A hex representation of the input with two hex digits per byte
+ */
+ public static String toUnformattedJsonHex(byte[] x) {
+ return "0x" + (x == null ? "" : Hex.toHexString(x));
+ }
+
+ /**
+ * Converts a byte array representing a quantity according to ethereum json-rpc specifications.
+ *
+ *
+ * 0x000AEF -> 0x2AEF
+ *
+ * 0x00 -> 0x0
+ * @param x A hex string with or without leading zeroes ("0x00AEF"). If null, it is considered as zero.
+ * @return A hex string without leading zeroes ("0xAEF")
+ */
+ public static String toQuantityJsonHex(byte[] x) {
+ String withoutLeading = LEADING_ZEROS_PATTERN.matcher(toJsonHex(x)).replaceFirst("0x");
+ if ("0x".equals(withoutLeading)) {
+ return "0x0";
+ }
+
+ return withoutLeading;
+ }
+
+ /**
+ * Converts "0x876AF" to "876AF"
+ */
+ public static String removeZeroX(String str) {
+ return str.substring(2);
+ }
+
+ public static long JSonHexToLong(String x) {
+ if (!x.startsWith("0x")) {
+ throw new NumberFormatException("Incorrect hex syntax");
+ }
+ x = x.substring(2);
+ return Long.parseLong(x, 16);
+ }
+}
+
diff --git a/src/main/java/io/xdag/utils/BasicAuth.java b/src/main/java/io/xdag/utils/BasicAuth.java
new file mode 100644
index 00000000..80452c39
--- /dev/null
+++ b/src/main/java/io/xdag/utils/BasicAuth.java
@@ -0,0 +1,48 @@
+package io.xdag.utils;
+
+import cn.hutool.core.lang.Pair;
+
+import java.util.Base64;
+
+/**
+ * Basic authentication helper.
+ *
+ */
+public class BasicAuth {
+
+ /**
+ * Parses the username and password from the AUTHORIZATION header.
+ *
+ * @param auth
+ * @return a pair of username and password if success, otherwise null
+ */
+ public static Pair parseAuth(String auth) {
+ try {
+ if (auth != null && auth.startsWith("Basic ")) {
+ String str = Bytes.toString(Base64.getDecoder().decode(auth.substring(6)));
+ int idx = str.indexOf(':');
+ if (idx != -1) {
+ return Pair.of(str.substring(0, idx), str.substring(idx + 1));
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ // invalid base64 string
+ }
+
+ return null;
+ }
+
+ /**
+ * Generates the AUTHORIZATION header.
+ *
+ * @param username
+ * @param password
+ * @return
+ */
+ public static String generateAuth(String username, String password) {
+ return "Basic " + Base64.getEncoder().encodeToString(Bytes.of(username + ":" + password));
+ }
+
+ private BasicAuth() {
+ }
+}
diff --git a/src/main/java/io/xdag/utils/BasicUtils.java b/src/main/java/io/xdag/utils/BasicUtils.java
index 24a25d0c..0526e488 100644
--- a/src/main/java/io/xdag/utils/BasicUtils.java
+++ b/src/main/java/io/xdag/utils/BasicUtils.java
@@ -31,6 +31,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.NumberFormat;
+import java.util.regex.Pattern;
import java.util.zip.CRC32;
public class BasicUtils {
@@ -186,4 +187,21 @@ public static BigDecimal xdag_hashrate(BigInteger[] diffs) {
public static double xdag_log_difficulty2hashrate(double logDiff) {
return Math.exp(logDiff) * Math.pow(2, -58) * (0.65);
}
+
+ /**
+ * @param number should be in form '0x34fabd34....'
+ * @return String
+ */
+ public static BigInteger unifiedNumericToBigInteger(String number) {
+
+ boolean match = Pattern.matches("0[xX][0-9a-fA-F]+", number);
+ if (!match) {
+ return (new BigInteger(number));
+ } else{
+ number = number.substring(2);
+ number = number.length() % 2 != 0 ? "0".concat(number) : number;
+ byte[] numberBytes = Hex.decode(number);
+ return (new BigInteger(1, numberBytes));
+ }
+ }
}
diff --git a/src/main/java/io/xdag/utils/Bytes.java b/src/main/java/io/xdag/utils/Bytes.java
new file mode 100644
index 00000000..23bd796d
--- /dev/null
+++ b/src/main/java/io/xdag/utils/Bytes.java
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2017-2020 The Semux Developers
+ *
+ * Distributed under the MIT software license, see the accompanying file
+ * LICENSE or https://opensource.org/licenses/mit-license.php
+ */
+package io.xdag.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+
+public class Bytes {
+
+ private static final SecureRandom secureRandom = new SecureRandom();
+
+ /**
+ * Default charset.
+ */
+ public static final String CHARSET = "UTF-8";
+
+ /**
+ * Empty byte array.
+ */
+ public static final byte[] EMPTY_BYTES = new byte[0];
+
+ /**
+ * Empty address.
+ */
+ public static final byte[] EMPTY_ADDRESS = new byte[20];
+
+ /**
+ * Empty 256-bit hash.
+ *
+ * Note: this is not the hash of empty byte array.
+ */
+ public static final byte[] EMPTY_HASH = new byte[32];
+
+ private Bytes() {
+ }
+
+ /**
+ * Generate a random byte array of required length.
+ *
+ * @param n
+ * @return
+ */
+ public static byte[] random(int n) {
+ byte[] bytes = new byte[n];
+ secureRandom.nextBytes(bytes);
+
+ return bytes;
+ }
+
+ /**
+ * Merge two byte arrays into one.
+ *
+ * @param b1
+ * @param b2
+ * @return
+ */
+ public static byte[] merge(byte[] b1, byte[] b2) {
+ byte[] res = new byte[b1.length + b2.length];
+ System.arraycopy(b1, 0, res, 0, b1.length);
+ System.arraycopy(b2, 0, res, b1.length, b2.length);
+
+ return res;
+ }
+
+ /**
+ * Merge byte array and byte
+ *
+ * @param b1
+ * @param b2
+ * @return
+ */
+ public static byte[] merge(byte[] b1, byte b2) {
+ byte[] res = new byte[b1.length + 1];
+ System.arraycopy(b1, 0, res, 0, b1.length);
+ res[b1.length] = b2;
+
+ return res;
+ }
+
+ /**
+ * Merge byte and byte array.
+ *
+ * @param b1
+ * @param b2
+ * @return
+ */
+ public static byte[] merge(byte b1, byte[] b2) {
+ byte[] res = new byte[1 + b2.length];
+ res[0] = b1;
+ System.arraycopy(b2, 0, res, 1, b2.length);
+
+ return res;
+ }
+
+ /**
+ * Merge byte arrays into one.
+ *
+ * @param bytes
+ * byte arrays
+ * @return
+ */
+ public static byte[] merge(byte[]... bytes) {
+ return merge(Arrays.asList(bytes));
+ }
+
+ /**
+ * Merge byte arrays into one.
+ *
+ * @param bytes
+ * byte arrays
+ * @return
+ */
+ public static byte[] merge(List bytes) {
+ int length = 0;
+ for (byte[] b : bytes) {
+ length += b.length;
+ }
+
+ byte[] res = new byte[length];
+ int i = 0;
+ for (byte[] b : bytes) {
+ System.arraycopy(b, 0, res, i, b.length);
+ i += b.length;
+ }
+
+ return res;
+ }
+
+ /**
+ * Convert string into an byte array.
+ *
+ * @param str
+ * @return
+ */
+ public static byte[] of(String str) {
+ try {
+ return str.getBytes(CHARSET);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Convert a byte into an byte array.
+ *
+ * @param b
+ * @return
+ */
+ public static byte[] of(byte b) {
+ return new byte[] { b };
+ }
+
+ /**
+ * Convert a short integer into an byte array.
+ *
+ * @param s
+ * @return
+ */
+ public static byte[] of(short s) {
+ byte[] bytes = new byte[2];
+ bytes[0] = (byte) ((s >> 8) & 0xff);
+ bytes[1] = (byte) (s & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Convert an integer into an byte array.
+ *
+ * @param i
+ * @return
+ */
+ public static byte[] of(int i) {
+ byte[] bytes = new byte[4];
+ bytes[0] = (byte) ((i >> 24) & 0xff);
+ bytes[1] = (byte) ((i >> 16) & 0xff);
+ bytes[2] = (byte) ((i >> 8) & 0xff);
+ bytes[3] = (byte) (i & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Convert a long integer into an byte array.
+ *
+ * @param i
+ * @return
+ */
+ public static byte[] of(long i) {
+ byte[] bytes = new byte[8];
+ bytes[0] = (byte) ((i >> 56) & 0xff);
+ bytes[1] = (byte) ((i >> 48) & 0xff);
+ bytes[2] = (byte) ((i >> 40) & 0xff);
+ bytes[3] = (byte) ((i >> 32) & 0xff);
+ bytes[4] = (byte) ((i >> 24) & 0xff);
+ bytes[5] = (byte) ((i >> 16) & 0xff);
+ bytes[6] = (byte) ((i >> 8) & 0xff);
+ bytes[7] = (byte) (i & 0xff);
+ return bytes;
+ }
+
+ /**
+ * Convert byte array into string.
+ *
+ * @param bytes
+ * @return
+ */
+ public static String toString(byte[] bytes) {
+ try {
+ return new String(bytes, CHARSET);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Covert byte array into a byte.
+ *
+ * @param bytes
+ * @return
+ */
+ public static byte toByte(byte[] bytes) {
+ return bytes[0];
+ }
+
+ /**
+ * Covert byte array into a short integer.
+ *
+ * @param bytes
+ * @return
+ */
+ public static short toShort(byte[] bytes) {
+ return (short) (((bytes[0] & 0xff) << 8) | (bytes[1] & 0xff));
+ }
+
+ /**
+ * Covert byte array into an integer.
+ *
+ * @param bytes
+ * @return
+ */
+ public static int toInt(byte[] bytes) {
+ return ((bytes[0] & 0xff) << 24)
+ | ((bytes[1] & 0xff) << 16)
+ | ((bytes[2] & 0xff) << 8)
+ | (bytes[3] & 0xff);
+ }
+
+ /**
+ * Covert byte array into a long integer.
+ *
+ * @param bytes
+ * @return
+ */
+ public static long toLong(byte[] bytes) {
+ return ((bytes[0] & 0xffL) << 56)
+ | ((bytes[1] & 0xffL) << 48)
+ | ((bytes[2] & 0xffL) << 40)
+ | ((bytes[3] & 0xffL) << 32)
+ | ((bytes[4] & 0xffL) << 24)
+ | ((bytes[5] & 0xffL) << 16)
+ | ((bytes[6] & 0xffL) << 8)
+ | (bytes[7] & 0xff);
+ }
+
+
+}
diff --git a/src/main/java/io/xdag/utils/BytesUtils.java b/src/main/java/io/xdag/utils/BytesUtils.java
index 5ede9dee..7856867b 100644
--- a/src/main/java/io/xdag/utils/BytesUtils.java
+++ b/src/main/java/io/xdag/utils/BytesUtils.java
@@ -33,6 +33,9 @@
public class BytesUtils {
+ public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+ private static final byte[] ZERO_BYTE_ARRAY = new byte[]{0};
+
public static byte[] intToBytes(int value, boolean littleEndian) {
ByteBuffer buffer = ByteBuffer.allocate(4);
if (littleEndian) {
@@ -305,4 +308,38 @@ public static double hexBytesToDouble(byte[] input, int offset, boolean littleEn
public String byteToBinaryString(byte b) {
return Integer.toBinaryString(b & 0xFF);
}
+
+ public static byte[] stripLeadingZeroes(byte[] data) {
+ return stripLeadingZeroes(data, ZERO_BYTE_ARRAY);
+ }
+
+ public static byte[] stripLeadingZeroes(byte[] data, byte[] valueForZero) {
+ if (data == null) {
+ return null;
+ }
+
+ final int firstNonZero = firstNonZeroByte(data);
+ switch (firstNonZero) {
+ case -1:
+ return valueForZero;
+
+ case 0:
+ return data;
+
+ default:
+ byte[] result = new byte[data.length - firstNonZero];
+ System.arraycopy(data, firstNonZero, result, 0, data.length - firstNonZero);
+
+ return result;
+ }
+ }
+
+ public static int firstNonZeroByte(byte[] data) {
+ for (int i = 0; i < data.length; ++i) {
+ if (data[i] != 0) {
+ return i;
+ }
+ }
+ return -1;
+ }
}
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index a3418cf9..0ee4dd9e 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -68,7 +68,7 @@
-
+
diff --git a/src/test/java/io/xdag/BlockBuilder.java b/src/test/java/io/xdag/BlockBuilder.java
index c70e7c10..5a3a2212 100644
--- a/src/test/java/io/xdag/BlockBuilder.java
+++ b/src/test/java/io/xdag/BlockBuilder.java
@@ -47,7 +47,7 @@ public static Block generateAddressBlock(Config config, ECKeyPair key, long xdag
}
public static Block generateExtraBlock(Config config, ECKeyPair key, long xdagTime, List pendings) {
- Block b = new Block(config, xdagTime, null, pendings, false, null, null, -1);
+ Block b = new Block(config, xdagTime, null, pendings, true, null, null, -1);
b.signOut(key);
byte[] random = Hash.sha256(Hex.decode("1234"));
b.setNonce(random);
@@ -55,7 +55,7 @@ public static Block generateExtraBlock(Config config, ECKeyPair key, long xdagTi
}
public static Block generateExtraBlockGivenRandom(Config config, ECKeyPair key, long xdagTime, List pendings, String randomS) {
- Block b = new Block(config, xdagTime, null, pendings, false, null, null, -1);
+ Block b = new Block(config, xdagTime, null, pendings, true, null, null, -1);
b.signOut(key);
byte[] random = Hash.sha256(Hex.decode(randomS));
b.setNonce(random);
diff --git a/src/test/java/io/xdag/core/BlockchainTest.java b/src/test/java/io/xdag/core/BlockchainTest.java
index 2a46073f..3da4dfba 100644
--- a/src/test/java/io/xdag/core/BlockchainTest.java
+++ b/src/test/java/io/xdag/core/BlockchainTest.java
@@ -257,6 +257,58 @@ public void testTransactionBlock() throws ParseException {
assertEquals("1124.0", String.valueOf(amount2xdag(toBlock.getInfo().getAmount())));
// block reword 1024 - 100 = 924.0
assertEquals("924.0", String.valueOf(amount2xdag(fromBlock.getInfo().getAmount())));
+
+
+ // test two key to use
+ // 4. make one transaction(100 XDAG) block(from No.1 mainblock to address block)
+ to = new Address(extraBlockList.get(0).getHashLow(), XDAG_FIELD_IN);
+ from = new Address(addressBlock.getHashLow(), XDAG_FIELD_OUT);
+ xdagTime = XdagTime.getEndOfEpoch(XdagTime.msToXdagtimestamp(generateTime));
+
+
+ List refs = Lists.newArrayList();
+ refs.add(new Address(from.getHashLow(), XdagField.FieldType.XDAG_FIELD_IN, xdag2amount(50.00))); // key1
+ refs.add(new Address(to.getHashLow(), XDAG_FIELD_OUT, xdag2amount(50.00)));
+ List keys = new ArrayList<>();
+ keys.add(addrKey);
+ Block b = new Block(config, xdagTime, refs, null, false, keys, null, -1); // orphan
+ b.signIn(addrKey);
+ b.signOut(poolKey);
+
+ txBlock = b;
+
+ // 4. local check
+ assertTrue(blockchain.canUseInput(txBlock));
+ // 5. remote check
+ assertTrue(blockchain.canUseInput(new Block(txBlock.getXdagBlock())));
+
+
+ result = blockchain.tryToConnect(txBlock);
+ // import transaction block, result may be IMPORTED_NOT_BEST or IMPORTED_BEST
+ assertTrue(result == IMPORTED_NOT_BEST || result == IMPORTED_BEST);
+ // there is 12 blocks and 10 mainblocks
+// assertChainStatus(12, 10, 1,1, blockchain);
+
+ pending.clear();
+ pending.add(new Address(txBlock.getHashLow()));
+ ref = extraBlockList.get(extraBlockList.size()-1).getHashLow();
+ // 4. confirm transaction block with 3 mainblocks
+ for(int i = 1; i <= 3; i++) {
+ generateTime += 64000L;
+ pending.add(new Address(ref, XDAG_FIELD_OUT));
+ long time = XdagTime.msToXdagtimestamp(generateTime);
+ xdagTime = XdagTime.getEndOfEpoch(time);
+ Block extraBlock = generateExtraBlock(config, poolKey, xdagTime, pending);
+ blockchain.tryToConnect(extraBlock);
+ ref = extraBlock.getHashLow();
+ extraBlockList.add(extraBlock);
+ pending.clear();
+ }
+
+ toBlock = blockchain.getBlockStore().getBlockInfoByHash(to.getHashLow());
+ fromBlock = blockchain.getBlockStore().getBlockInfoByHash(from.getHashLow());
+ assertEquals("974.0", String.valueOf(amount2xdag(toBlock.getInfo().getAmount())));
+ assertEquals("1074.0", String.valueOf(amount2xdag(fromBlock.getInfo().getAmount())));
}
@Test
@@ -368,8 +420,8 @@ public void testOriginFork() throws ParseException {
BigInteger privateKey = new BigInteger(privString, 16);
- String firstDiff = "50372dcc7b";
- String secondDiff = "7fd767e345";
+ String firstDiff = "aedbac91e1";
+ String secondDiff = "d936e19b89";
ECKeyPair addrKey = ECKeyPair.create(privateKey);
ECKeyPair poolKey = ECKeyPair.create(privateKey);
diff --git a/src/test/java/io/xdag/core/RandomXSyncTest.java b/src/test/java/io/xdag/core/RandomXSyncTest.java
index ff8049ad..81653d9e 100644
--- a/src/test/java/io/xdag/core/RandomXSyncTest.java
+++ b/src/test/java/io/xdag/core/RandomXSyncTest.java
@@ -76,7 +76,7 @@ public void init() {
RandomXConstants.SEEDHASH_EPOCH_TESTNET_BLOCKS = 64;
RandomXConstants.RANDOMX_TESTNET_FORK_HEIGHT = 128;
RandomXConstants.SEEDHASH_EPOCH_TESTNET_LAG = 4;
- forkHeight = 3;
+ forkHeight = 2;
}
From 0fb0f39986fd13a38e705f05340210efd471aca8 Mon Sep 17 00:00:00 2001
From: punk8 <1401501690@qq.com>
Date: Tue, 25 May 2021 10:28:26 +0800
Subject: [PATCH 02/11] add rpc modules config
---
docs/README_zh.md | 72 +++++--
...ation_RandomX_Algorithm_Environment_eng.md | 8 +-
...ration_RandomX_Algorithm_Environment_zh.md | 9 +-
pom.xml | 10 +
src/main/java/io/xdag/Kernel.java | 55 +++--
.../java/io/xdag/config/AbstractConfig.java | 58 +++---
.../java/io/xdag/config/TestnetConfig.java | 2 +-
src/main/java/io/xdag/rpc/Web3.java | 4 +-
src/main/java/io/xdag/rpc/Web3Impl.java | 151 ++++++++++++++
.../java/io/xdag/rpc/dto/BlockResultDTO.java | 7 +
.../io/xdag/rpc/jsonrpc/JsonRpcMessage.java | 2 +
.../xdag/rpc/modules/XdagJsonRpcRequest.java | 2 -
.../modules/XdagJsonRpcRequestVisitor.java | 7 -
.../xdag/rpc/modules/web3/Web3XdagModule.java | 2 +
.../rpc/modules/web3/Web3XdagModuleImpl.java | 32 ++-
.../xdag/subscribe/SubscriptionId.java | 52 +++++
.../xdag/subscribe/XDAGSubscribeRequest.java | 47 +++++
.../subscribe/XDAGUnsubscribeRequest.java | 4 +
.../xdag/subscribe/XdagSubscribeParams.java | 4 +-
.../XdagSubscribeParamsDeserializer.java | 60 ++++++
.../subscribe/XdagSubscribeParamsVisitor.java | 26 +--
.../XdagSubscriptionNotificationDTO.java | 8 +-
.../xdag/rpc/netty/Web3WebSocketServer.java | 194 +++++++++---------
.../io/xdag/rpc/netty/XdagJsonRpcHandler.java | 120 ++++++-----
src/main/java/io/xdag/utils/BasicUtils.java | 110 +++++-----
src/main/resources/rpc_modules.conf | 96 +++++++++
src/main/resources/xdag-testnet.config | 58 ++++++
.../java/io/xdag/core/BlockchainTest.java | 23 ++-
.../java/io/xdag/core/ExtraBlockTest.java | 7 +-
.../java/io/xdag/core/RandomXSyncTest.java | 4 +-
src/test/java/io/xdag/core/RewardTest.java | 6 +-
.../jsonrpc/JsonRpcResultResponseTest.java | 24 +++
32 files changed, 935 insertions(+), 329 deletions(-)
create mode 100644 src/main/java/io/xdag/rpc/Web3Impl.java
create mode 100644 src/main/java/io/xdag/rpc/modules/xdag/subscribe/SubscriptionId.java
create mode 100644 src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGSubscribeRequest.java
create mode 100644 src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGUnsubscribeRequest.java
create mode 100644 src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsDeserializer.java
create mode 100644 src/main/resources/rpc_modules.conf
create mode 100644 src/test/java/io/xdag/rpc/jsonrpc/JsonRpcResultResponseTest.java
diff --git a/docs/README_zh.md b/docs/README_zh.md
index f0d3f00b..e2a0f842 100644
--- a/docs/README_zh.md
+++ b/docs/README_zh.md
@@ -9,6 +9,7 @@
- [系统环境](#系统环境)
- [安装与用法](#安装与用法)
- [发展](#发展)
+ - [火星计划](#火星计划)
- [代码规范](#代码规范)
- [贡献](#贡献)
- [赞助](#赞助)
@@ -36,25 +37,58 @@ XDAGJ教程可以让您快速加入并体验XDAGJ的钱包及挖矿功能,私
XDAGJ已经具备了一个矿池基本的功能,后续的工作继续优化现有代码,提升系统的稳定性。及时吸纳优秀的区块链技术,不断为XDAG注入新鲜血液
-下一阶段的主要工作包括但不限于下述内容
-
-- 优化XDAG共识流程以及同步协议
-- 使用Libp2p替换当前的dnet网络,在系统稳定性允许的前提下逐步提高去中心化程度
-- 开放API接口,提供黑盒测试
-- 提供各类符合Web3j规范的RPC接口
-- 为交易加入手续费,提高用户的参与度
-- 利用BIPxx等规范改进现有公私钥及地址产生方式,提供一种更为方便且通用的密钥存储方式
-- 添加快照功能,解决历史主块加载速度慢的问题
-- 优化地址块结构,避免粉尘攻击
-
-
-与此同时,我们也在积极的为XDAG更多的应用场景,包括但不限于以下内容
-
-- 探索使用neo4j作为存储层的可能,提供一种可视化的DAG存储
-- 探索一种在XDAG中加入虚拟机,实现智能合约的方法,提高系统的可用性
-- 探索有效的跨链方案,打破XDAG的封闭生态
-
-
+### 火星计划
+
+火星计划优先解决社区关注的技术问题,期间研究有重大突破可灵活加入开发计划,最新阶段性计划如下:
+
+#### 探索阶段:XDAGJ测试网上线(已上线,公测中)
+
+- [x] 部署XDAGJ测试网环境,开放公测
+
+- [x] 接入RandomX算法
+
+- [x] 接入libp2p协议
+
+- [x] 测试网区块链浏览器
+
+- [x] 测试币获取
+
+
+#### 登陆阶段:XDAGJ主网上线
+
+- [ ] 完善测试案例:逐步完善现有功能的测试案例
+
+- [ ] 完善日志功能:提供较为完整的日志功能,便于后期问题排查
+
+- [ ] 优化同步协议:改进现有的同步协议,提高同步效率
+
+- [ ] 实现快照功能:解决历史区块过多所造成的加载数据耗时较长问题
+
+- [ ] 实现RPC功能:接入Web3j,实现接口的规范化
+
+- [ ] 挖矿协议改进:引入较成熟的Stratum协议,方便矿机的接入与使用
+
+- [ ] 轻量级钱包应用:接入MateMask,加入浏览器钱包
+
+- [ ] 规范公私钥格式,遵循BIPXX规范,加入助记词方式生成公私钥对
+
+#### 拓展阶段:XDAGJ & EVM 拓展
+
+- [ ] 修改地址块结构,增加手续费
+
+- [ ] 优化改善移动端钱包,提高用户体验
+
+- [ ] 开放智能合约,实现支持Solidity的EVM,兼容以太坊智能合约
+
+- [ ] 降低矿池门槛,逐步开放白名单从而实现完全中心化
+
+#### 繁荣阶段:DAG & DeFi
+
+- [ ] 跨链:兼容多种区块链系统接入,实现XDAG与其它链世界的互通
+
+- [ ] 加入预言机
+
+- [ ] 加入分布式交易所
## 代码规范
diff --git a/docs/Win10_Configuration_RandomX_Algorithm_Environment_eng.md b/docs/Win10_Configuration_RandomX_Algorithm_Environment_eng.md
index 310a1b31..65e4a1df 100644
--- a/docs/Win10_Configuration_RandomX_Algorithm_Environment_eng.md
+++ b/docs/Win10_Configuration_RandomX_Algorithm_Environment_eng.md
@@ -22,7 +22,7 @@ pause
- Modify the above file to xxx.bat file, then right-click to run as an administrator, and wait for the program to end
-![Open the security group](./Open_the_security_group.png)
+![Open the security group](img/Open_the_security_group.png)
### Open hugeoage
@@ -30,12 +30,12 @@ pause
- Find the `windows management tool`, and open it after going to the `local security policy`
-![RandomX_first](./RandomX_first.png)
+![RandomX_first](img/RandomX_first.png)
- Click `Local Policies`->`User Rights Assignment`->`Lock Memory Pages`
-![RandomX_two](./RandomX_two.png)
+![RandomX_two](img/RandomX_two.png)
- Add the computer user name for the locked memory page, restart the computer after confirmation
-![RandomX_three](./RandomX_three.png)
+![RandomX_three](img/RandomX_three.png)
diff --git a/docs/Win10_Configuration_RandomX_Algorithm_Environment_zh.md b/docs/Win10_Configuration_RandomX_Algorithm_Environment_zh.md
index 037e8810..56cd5619 100644
--- a/docs/Win10_Configuration_RandomX_Algorithm_Environment_zh.md
+++ b/docs/Win10_Configuration_RandomX_Algorithm_Environment_zh.md
@@ -22,7 +22,7 @@ pause
- 修改上述文件为xxx.bat文件,然后右键用管理员身份运行,等待程序运行结束即可
-![Open the security group](./Open_the_security_group.png)
+![Open the security group](img/Open_the_security_group.png)
## 开启hugeoage功能
@@ -30,14 +30,13 @@ pause
- 找到`windows管理工具`,并在里面到`本地安全策略`后打开
-![RandomX_first](./RandomX_first.png)
+![RandomX_first](img/RandomX_first.png)
- 点击`本地策略`->`用户权限分配`->`锁定内存页`
-![RandomX_two](./RandomX_two.png)
+![RandomX_two](img/RandomX_two.png)
- 为锁定内存页添加计算机用户名,确认之后重启电脑
-![RandomX_three](./RandomX_three.png)
+![RandomX_three](img/RandomX_three.png)
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e1f89792..3bae316f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -581,6 +581,16 @@
+
+
+
+ com.typesafe
+ config
+ 1.4.1
+
+
+
+
diff --git a/src/main/java/io/xdag/Kernel.java b/src/main/java/io/xdag/Kernel.java
index 05570b2b..bfb7e1d1 100644
--- a/src/main/java/io/xdag/Kernel.java
+++ b/src/main/java/io/xdag/Kernel.java
@@ -60,8 +60,13 @@
import io.xdag.net.node.NodeManager;
import io.xdag.randomx.RandomX;
import io.xdag.rpc.Web3;
+import io.xdag.rpc.Web3Impl;
import io.xdag.rpc.cors.CorsConfiguration;
+import io.xdag.rpc.modules.web3.Web3XdagModule;
import io.xdag.rpc.modules.web3.Web3XdagModuleImpl;
+import io.xdag.rpc.modules.xdag.XdagModule;
+import io.xdag.rpc.modules.xdag.XdagModuleTransactionDisabled;
+import io.xdag.rpc.modules.xdag.XdagModuleWalletDisabled;
import io.xdag.rpc.netty.*;
import io.xdag.rpc.serialize.JacksonBasedRpcSerializer;
import io.xdag.rpc.serialize.JsonRpcSerializer;
@@ -126,7 +131,7 @@ public class Kernel {
// rpc
protected JsonRpcWeb3ServerHandler jsonRpcWeb3ServerHandler;
protected Web3 web3;
-// protected Web3WebSocketServer web3WebSocketServer;
+ protected Web3WebSocketServer web3WebSocketServer;
protected Web3HttpServer web3HttpServer;
protected JsonRpcWeb3FilterHandler jsonRpcWeb3FilterHandler;
protected JacksonBasedRpcSerializer jacksonBasedRpcSerializer;
@@ -295,6 +300,12 @@ public synchronized void testStart() throws Exception {
xdagState = XdagState.WTST;
}
+ // ====================================
+ // rpc start
+ // ====================================
+ getWeb3HttpServer().start();
+ getWeb3WebSocketServer().start();
+
// ====================================
// telnet server
// ====================================
@@ -312,7 +323,8 @@ private Web3 getWeb3() {
}
private Web3 buildWeb3() {
- return null;
+ Web3XdagModule web3XdagModule = new Web3XdagModuleImpl(this.getBlockchain(),new XdagModule((byte) 0x1,new XdagModuleWalletDisabled(),new XdagModuleTransactionDisabled()),this);
+ return new Web3Impl(web3XdagModule);
}
private JsonRpcWeb3ServerHandler getJsonRpcWeb3ServerHandler() {
@@ -325,26 +337,26 @@ private JsonRpcWeb3ServerHandler getJsonRpcWeb3ServerHandler() {
return jsonRpcWeb3ServerHandler;
}
-//
-// private Web3WebSocketServer getWeb3WebSocketServer() throws UnknownHostException {
-// if (web3WebSocketServer == null) {
-// JsonRpcSerializer jsonRpcSerializer = getJsonRpcSerializer();
-// XdagJsonRpcHandler jsonRpcHandler = new XdagJsonRpcHandler(jsonRpcSerializer);
-// web3WebSocketServer = new Web3WebSocketServer(
-// InetAddress.getLocalHost(),
-// 4444,
-// jsonRpcHandler,
-// getJsonRpcWeb3ServerHandler()
-// );
-// }
-//
-// return web3WebSocketServer;
-// }
+
+ private Web3WebSocketServer getWeb3WebSocketServer() throws UnknownHostException {
+ if (web3WebSocketServer == null) {
+ JsonRpcSerializer jsonRpcSerializer = getJsonRpcSerializer();
+ XdagJsonRpcHandler jsonRpcHandler = new XdagJsonRpcHandler(jsonRpcSerializer);
+ web3WebSocketServer = new Web3WebSocketServer(
+ InetAddress.getByName("127.0.0.1"),
+ 4444,
+ jsonRpcHandler,
+ getJsonRpcWeb3ServerHandler()
+ );
+ }
+
+ return web3WebSocketServer;
+ }
private Web3HttpServer getWeb3HttpServer() throws UnknownHostException {
if (web3HttpServer == null) {
web3HttpServer = new Web3HttpServer(
- InetAddress.getLocalHost(),
+ InetAddress.getByName("127.0.0.1"),
4445,
123,
true,
@@ -361,7 +373,7 @@ private JsonRpcWeb3FilterHandler getJsonRpcWeb3FilterHandler() throws UnknownHos
if (jsonRpcWeb3FilterHandler == null) {
jsonRpcWeb3FilterHandler = new JsonRpcWeb3FilterHandler(
"*",
- InetAddress.getLocalHost(),
+ InetAddress.getByName("127.0.0.1"),
null
);
}
@@ -385,6 +397,11 @@ public synchronized void testStop() {
isRunning.set(false);
+ //
+ if (web3HttpServer != null) {
+ web3HttpServer.stop();
+ }
+
// 1. 工作层关闭
// stop consensus
sync.stop();
diff --git a/src/main/java/io/xdag/config/AbstractConfig.java b/src/main/java/io/xdag/config/AbstractConfig.java
index ca335da2..c4878acf 100644
--- a/src/main/java/io/xdag/config/AbstractConfig.java
+++ b/src/main/java/io/xdag/config/AbstractConfig.java
@@ -24,6 +24,8 @@
package io.xdag.config;
import cn.hutool.setting.Setting;
+import com.typesafe.config.ConfigFactory;
+import com.typesafe.config.ConfigObject;
import io.xdag.config.spec.*;
import io.xdag.core.XdagField;
import io.xdag.crypto.DnetKeys;
@@ -340,34 +342,40 @@ public List getRpcModules() {
List modules = new ArrayList<>();
- // TODO: get modules from config
-// if (!configFromFiles.hasPath("rpc.modules")) {
-// return modules;
-// }
-//
-// List extends ConfigObject> list = configFromFiles.getObjectList("rpc.modules");
-//
-// for (ConfigObject configObject : list) {
-// Config configElement = configObject.toConfig();
-// String name = configElement.getString("name");
-// String version = configElement.getString("version");
-// boolean enabled = configElement.getBoolean("enabled");
-// List enabledMethods = null;
-// List disabledMethods = null;
-//
-// if (configElement.hasPath("methods.enabled")) {
-// enabledMethods = configElement.getStringList("methods.enabled");
-// }
-//
-// if (configElement.hasPath("methods.disabled")) {
-// disabledMethods = configElement.getStringList("methods.disabled");
-// }
-//
-// modules.add(new ModuleDescription(name, version, enabled, enabledMethods, disabledMethods));
-// }
+ com.typesafe.config.Config configFromFiles = ConfigFactory.load("rpc_modules");
+ List extends ConfigObject> list = configFromFiles.getObjectList("rpc.modules");
+
+ for (ConfigObject configObject : list) {
+ com.typesafe.config.Config configElement = configObject.toConfig();
+ String name = configElement.getString("name");
+ String version = configElement.getString("version");
+ boolean enabled = configElement.getBoolean("enabled");
+ List enabledMethods = null;
+ List disabledMethods = null;
+
+ if (configElement.hasPath("methods.enabled")) {
+ enabledMethods = configElement.getStringList("methods.enabled");
+ }
+
+ if (configElement.hasPath("methods.disabled")) {
+ disabledMethods = configElement.getStringList("methods.disabled");
+ }
+
+ modules.add(new ModuleDescription(name, version, enabled, enabledMethods, disabledMethods));
+ }
this.moduleDescriptions = modules;
+ // TODO: get modules from config
+// String name = "xdag";
+// String version = "1.0";
+// boolean enabled = true;
+// List enabledMethods = null;
+// List disabledMethods = null;
+//
+// modules.add(new ModuleDescription(name, version, enabled, enabledMethods, disabledMethods));
+// this.moduleDescriptions = modules;
+
return modules;
}
}
diff --git a/src/main/java/io/xdag/config/TestnetConfig.java b/src/main/java/io/xdag/config/TestnetConfig.java
index 4e9d5418..4dedf91f 100644
--- a/src/main/java/io/xdag/config/TestnetConfig.java
+++ b/src/main/java/io/xdag/config/TestnetConfig.java
@@ -33,7 +33,7 @@ public TestnetConfig() {
this.whitelistUrl = "https://raw.githubusercontent.com/XDagger/xdag/master/client/netdb-white-testnet.txt";
// testnet wait 1 epoch
- this.waitEpoch = 10;
+ this.waitEpoch = 1;
this.xdagEra = 0x16900000000L;
this.mainStartAmount = UnsignedLong.fromLongBits(1L << 42).longValue();
diff --git a/src/main/java/io/xdag/rpc/Web3.java b/src/main/java/io/xdag/rpc/Web3.java
index a5b7e56e..78e91e37 100644
--- a/src/main/java/io/xdag/rpc/Web3.java
+++ b/src/main/java/io/xdag/rpc/Web3.java
@@ -1,9 +1,11 @@
package io.xdag.rpc;
+import io.xdag.rpc.modules.web3.Web3XdagModule;
+
import java.util.Arrays;
import java.util.Map;
-public interface Web3 {
+public interface Web3 extends Web3XdagModule {
class CallArguments {
public String from;
public String to;
diff --git a/src/main/java/io/xdag/rpc/Web3Impl.java b/src/main/java/io/xdag/rpc/Web3Impl.java
new file mode 100644
index 00000000..471da0b3
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/Web3Impl.java
@@ -0,0 +1,151 @@
+package io.xdag.rpc;
+
+import io.xdag.rpc.dto.BlockResultDTO;
+import io.xdag.rpc.modules.web3.Web3XdagModule;
+import io.xdag.rpc.modules.xdag.XdagModule;
+
+import java.util.Map;
+
+public class Web3Impl implements Web3{
+
+ Web3XdagModule web3XdagModule;
+
+ public Web3Impl(Web3XdagModule web3XdagModule) {
+ this.web3XdagModule = web3XdagModule;
+ }
+
+ @Override
+ public String web3_clientVersion() {
+ return null;
+ }
+
+ @Override
+ public String web3_sha3(String data) throws Exception {
+ return null;
+ }
+
+ @Override
+ public String net_version() {
+ return null;
+ }
+
+ @Override
+ public String net_peerCount() {
+ return null;
+ }
+
+ @Override
+ public boolean net_listening() {
+ return false;
+ }
+
+ @Override
+ public String[] net_peerList() {
+ return new String[0];
+ }
+
+ @Override
+ public Map rpc_modules() {
+ return null;
+ }
+
+ @Override
+ public void db_putString() {
+
+ }
+
+ @Override
+ public void db_getString() {
+
+ }
+
+ @Override
+ public void db_putHex() {
+
+ }
+
+ @Override
+ public void db_getHex() {
+
+ }
+
+ @Override
+ public String personal_newAccount(String passphrase) {
+ return null;
+ }
+
+ @Override
+ public String[] personal_listAccounts() {
+ return new String[0];
+ }
+
+ @Override
+ public String personal_importRawKey(String key, String passphrase) {
+ return null;
+ }
+
+ @Override
+ public String personal_sendTransaction(CallArguments transactionArgs, String passphrase) throws Exception {
+ return null;
+ }
+
+ @Override
+ public boolean personal_unlockAccount(String key, String passphrase, String duration) {
+ return false;
+ }
+
+ @Override
+ public boolean personal_lockAccount(String key) {
+ return false;
+ }
+
+ @Override
+ public String personal_dumpRawKey(String address) throws Exception {
+ return null;
+ }
+
+ @Override
+ public XdagModule getXdagModule() {
+ return web3XdagModule.getXdagModule();
+ }
+
+ @Override
+ public String xdag_protocolVersion() {
+ return web3XdagModule.xdag_protocolVersion();
+ }
+
+ @Override
+ public Object xdag_syncing() {
+ return web3XdagModule.xdag_syncing();
+ }
+
+ @Override
+ public String xdag_coinbase() {
+ return web3XdagModule.xdag_coinbase();
+ }
+
+ @Override
+ public String xdag_blockNumber() {
+ return web3XdagModule.xdag_blockNumber();
+ }
+
+ @Override
+ public String xdag_getBalance(String address) throws Exception {
+ return web3XdagModule.xdag_getBalance(address);
+ }
+
+ @Override
+ public String xdag_getTotalBalance() throws Exception {
+ return web3XdagModule.xdag_getTotalBalance();
+ }
+
+ @Override
+ public BlockResultDTO xdag_getBlockByNumber(String bnOrId, Boolean full) throws Exception {
+ return web3XdagModule.xdag_getBlockByNumber(bnOrId,full);
+ }
+
+ @Override
+ public BlockResultDTO xdag_getBlockByHash(String blockHash, Boolean full) throws Exception {
+ return web3XdagModule.xdag_getBlockByHash(blockHash,full);
+ }
+}
diff --git a/src/main/java/io/xdag/rpc/dto/BlockResultDTO.java b/src/main/java/io/xdag/rpc/dto/BlockResultDTO.java
index 301effdc..56c4cd9c 100644
--- a/src/main/java/io/xdag/rpc/dto/BlockResultDTO.java
+++ b/src/main/java/io/xdag/rpc/dto/BlockResultDTO.java
@@ -10,6 +10,13 @@ public class BlockResultDTO {
// blockInfo
// rawData
+ String height;
+ String data;
+ public BlockResultDTO(long height) {
+ this.height = Long.toHexString(height);
+ this.data = "hello";
+ }
+
public static BlockResultDTO fromBlock(Block b, boolean raw) {
diff --git a/src/main/java/io/xdag/rpc/jsonrpc/JsonRpcMessage.java b/src/main/java/io/xdag/rpc/jsonrpc/JsonRpcMessage.java
index 780fa500..c177fd09 100644
--- a/src/main/java/io/xdag/rpc/jsonrpc/JsonRpcMessage.java
+++ b/src/main/java/io/xdag/rpc/jsonrpc/JsonRpcMessage.java
@@ -2,7 +2,9 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+@JsonPropertyOrder({"jsonrpc", "id", "method", "result", "params", "error"})
public abstract class JsonRpcMessage {
private final JsonRpcVersion version;
diff --git a/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequest.java b/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequest.java
index f8fc1b85..2691db11 100644
--- a/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequest.java
+++ b/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequest.java
@@ -10,8 +10,6 @@
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "method", visible = true)
@JsonSubTypes({
-// @JsonSubTypes.Type(value = EthSubscribeRequest.class, name = "eth_subscribe"),
-// @JsonSubTypes.Type(value = EthUnsubscribeRequest.class, name = "eth_unsubscribe"),
})
public abstract class XdagJsonRpcRequest extends JsonRpcRequest {
public XdagJsonRpcRequest(
diff --git a/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequestVisitor.java b/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequestVisitor.java
index 1c76a274..4ae8ea0d 100644
--- a/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequestVisitor.java
+++ b/src/main/java/io/xdag/rpc/modules/XdagJsonRpcRequestVisitor.java
@@ -1,12 +1,5 @@
package io.xdag.rpc.modules;
-import io.netty.channel.ChannelHandlerContext;
-import io.xdag.rpc.jsonrpc.JsonRpcResultOrError;
-//import io.xdag.rpc.modules.eth.subscribe.EthSubscribeRequest;
-//import io.xdag.rpc.modules.eth.subscribe.EthUnsubscribeRequest;
public interface XdagJsonRpcRequestVisitor {
-// JsonRpcResultOrError visit(EthUnsubscribeRequest request, ChannelHandlerContext ctx);
-//
-// JsonRpcResultOrError visit(EthSubscribeRequest request, ChannelHandlerContext ctx);
}
diff --git a/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModule.java b/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModule.java
index 0e4a0c9b..9cce248e 100644
--- a/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModule.java
+++ b/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModule.java
@@ -32,6 +32,8 @@ default String xdag_chainId() {
String xdag_getBalance(String address) throws Exception;
+ String xdag_getTotalBalance() throws Exception;
+
default BlockResultDTO xdag_getTransactionByHash(String hash, Boolean full)throws Exception{
return xdag_getBlockByHash(hash,full);
}
diff --git a/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModuleImpl.java b/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModuleImpl.java
index 29a1f11f..35526b9d 100644
--- a/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModuleImpl.java
+++ b/src/main/java/io/xdag/rpc/modules/web3/Web3XdagModuleImpl.java
@@ -2,8 +2,11 @@
import com.sun.jdi.LongValue;
import io.xdag.Kernel;
+import io.xdag.config.Config;
+import io.xdag.config.MainnetConfig;
import io.xdag.core.Block;
import io.xdag.core.Blockchain;
+import io.xdag.core.XdagState;
import io.xdag.rpc.dto.BlockResultDTO;
import io.xdag.rpc.modules.xdag.XdagModule;
import io.xdag.utils.StringUtils;
@@ -51,12 +54,20 @@ public String xdag_protocolVersion() {
@Override
public Object xdag_syncing() {
long currentBlock = this.blockchain.getXdagStats().nmain;
- long highestBlock = this.blockchain.getXdagStats().totalnmain;
+ long highestBlock = Math.max(this.blockchain.getXdagStats().totalnmain, currentBlock);
- if (highestBlock < currentBlock){
- return false;
+ Config config = kernel.getConfig();
+ if (config instanceof MainnetConfig) {
+ if (kernel.getXdagState() != XdagState.SYNC){
+ return false;
+ }
+ } else {
+ if (kernel.getXdagState() != XdagState.STST) {
+ return false;
+ }
}
+
SyncingResult s = new SyncingResult();
try {
s.currentBlock = toQuantityJsonHex(currentBlock);
@@ -96,9 +107,22 @@ public String xdag_getBalance(String address) throws Exception {
return toQuantityJsonHex(balance);
}
+ @Override
+ public String xdag_getTotalBalance() throws Exception {
+ double balance = amount2xdag(kernel.getBlockchain().getXdagStats().getBalance());
+ return toQuantityJsonHex(balance);
+ }
+
@Override
public BlockResultDTO xdag_getBlockByNumber(String bnOrId, Boolean full) throws Exception {
- return null;
+ System.out.println(bnOrId);
+ System.out.println(full);
+ if (full) {
+ System.out.println("hello");
+ }
+ BlockResultDTO blockResultDTO = new BlockResultDTO(Integer.parseInt(bnOrId));
+
+ return blockResultDTO;
}
@Override
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/SubscriptionId.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/SubscriptionId.java
new file mode 100644
index 00000000..672168b9
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/SubscriptionId.java
@@ -0,0 +1,52 @@
+//package io.xdag.rpc.modules.xdag.subscribe;
+//
+//import com.fasterxml.jackson.annotation.JsonCreator;
+//import com.fasterxml.jackson.annotation.JsonValue;
+//import io.xdag.rpc.jsonrpc.JsonRpcResult;
+//import io.xdag.rpc.utils.TypeConverter;
+//
+//import java.security.SecureRandom;
+//import java.util.Arrays;
+//
+//public class SubscriptionId extends JsonRpcResult {
+// private final byte[] id;
+//
+// @JsonCreator
+// public SubscriptionId(String hexId) {
+// this.id = TypeConverter.stringHexToByteArray(hexId);
+// }
+//
+// public SubscriptionId() {
+// this.id = new byte[16];
+// new SecureRandom().nextBytes(id);
+// }
+//
+// public byte[] getId() {
+// return Arrays.copyOf(id, id.length);
+// }
+//
+// @Override
+// public int hashCode() {
+// return Arrays.hashCode(id);
+// }
+//
+// @Override
+// public boolean equals(Object o) {
+// if (o == this) {
+// return true;
+// }
+//
+// if (!(o instanceof SubscriptionId)) {
+// return false;
+// }
+//
+// SubscriptionId other = (SubscriptionId) o;
+// return Arrays.equals(this.id, other.id);
+// }
+//
+// @JsonValue
+// @SuppressWarnings("unused")
+// private String serialize() {
+// return TypeConverter.toJsonHex(id);
+// }
+//}
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGSubscribeRequest.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGSubscribeRequest.java
new file mode 100644
index 00000000..b3125962
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGSubscribeRequest.java
@@ -0,0 +1,47 @@
+//package io.xdag.rpc.modules.xdag.subscribe;
+//
+//import com.fasterxml.jackson.annotation.JsonCreator;
+//import com.fasterxml.jackson.annotation.JsonInclude;
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//import io.netty.channel.ChannelHandlerContext;
+//import io.xdag.rpc.jsonrpc.JsonRpcResultOrError;
+//import io.xdag.rpc.jsonrpc.JsonRpcVersion;
+//import io.xdag.rpc.modules.XdagJsonRpcMethod;
+//import io.xdag.rpc.modules.XdagJsonRpcRequest;
+//import io.xdag.rpc.modules.XdagJsonRpcRequestVisitor;
+//
+//import java.util.Objects;
+//
+//public class XDAGSubscribeRequest extends XdagJsonRpcRequest {
+//
+// private final XdagSubscribeParams params;
+//
+// @JsonCreator
+// public XDAGSubscribeRequest(
+// @JsonProperty("jsonrpc") JsonRpcVersion version,
+// @JsonProperty("method") XdagJsonRpcMethod method,
+// @JsonProperty("id") Integer id,
+// @JsonProperty("params") XdagSubscribeParams params) {
+// super(version, verifyMethod(method), Objects.requireNonNull(id));
+// this.params = Objects.requireNonNull(params);
+// }
+//
+// @JsonInclude(JsonInclude.Include.NON_NULL)
+// public XdagSubscribeParams getParams() {
+// return params;
+// }
+//
+// @Override
+// public JsonRpcResultOrError accept(XdagJsonRpcRequestVisitor visitor, ChannelHandlerContext ctx) {
+// return visitor.visit(this, ctx);
+// }
+//
+// private static XdagJsonRpcMethod verifyMethod(XdagJsonRpcMethod method) {
+// if (method != XdagJsonRpcMethod.ETH_SUBSCRIBE) {
+// throw new IllegalArgumentException(
+// "Wrong method mapped to eth_subscribe. Check JSON mapping configuration in JsonRpcRequest."
+// );
+// }
+// return method;
+// }
+//}
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGUnsubscribeRequest.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGUnsubscribeRequest.java
new file mode 100644
index 00000000..e1d285b6
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XDAGUnsubscribeRequest.java
@@ -0,0 +1,4 @@
+//package io.xdag.rpc.modules.xdag.subscribe;
+//
+//public class XDAGUnsubscribeRequest {
+//}
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParams.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParams.java
index bef665d9..124b8377 100644
--- a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParams.java
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParams.java
@@ -5,10 +5,10 @@
//import io.netty.channel.Channel;
//
//
-//@JsonDeserialize(using = EthSubscribeParamsDeserializer.class)
+//@JsonDeserialize(using = XdagSubscribeParamsDeserializer.class)
//@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
//public interface XdagSubscribeParams {
-// SubscriptionId accept(EthSubscribeParamsVisitor visitor, Channel channel);
+// SubscriptionId accept(XdagSubscribeParamsVisitor visitor, Channel channel);
//}
//
//
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsDeserializer.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsDeserializer.java
new file mode 100644
index 00000000..52945d35
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsDeserializer.java
@@ -0,0 +1,60 @@
+//package io.xdag.rpc.modules.xdag.subscribe;
+//
+//import com.fasterxml.jackson.core.JsonParser;
+//import com.fasterxml.jackson.core.JsonToken;
+//import com.fasterxml.jackson.databind.DeserializationContext;
+//import com.fasterxml.jackson.databind.JsonDeserializer;
+//
+//import java.io.IOException;
+//import java.util.HashMap;
+//
+//public class XdagSubscribeParamsDeserializer extends JsonDeserializer {
+//
+// private final HashMap> subscriptionTypes;
+//
+// public XdagSubscribeParamsDeserializer() {
+// this.subscriptionTypes = new HashMap<>();
+//// this.subscriptionTypes.put("newHeads", EthSubscribeNewHeadsParams.class);
+//// this.subscriptionTypes.put("logs", EthSubscribeLogsParams.class);
+// }
+//
+// @Override
+// public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+// if (!p.isExpectedStartArrayToken()) {
+// return ctxt.handleUnexpectedToken(
+// XdagSubscribeParams.class,
+// p.currentToken(),
+// p,
+// "xdag_subscribe parameters are expected to be arrays"
+// );
+// }
+// p.nextToken(); // skip '['
+// String subscriptionType = p.getText();
+// Class extends XdagSubscribeParams> subscriptionTypeClass = subscriptionTypes.get(subscriptionType);
+// p.nextToken();
+// XdagSubscribeParams params;
+// if (p.isExpectedStartObjectToken()) {
+// params = p.readValueAs(subscriptionTypeClass);
+// p.nextToken();
+// } else {
+// try {
+// params = subscriptionTypeClass.newInstance();
+// } catch (InstantiationException | IllegalAccessException e) {
+// return ctxt.handleInstantiationProblem(
+// subscriptionTypeClass,
+// null,
+// e
+// );
+// }
+// }
+// if (p.currentToken() != JsonToken.END_ARRAY) {
+// return ctxt.handleUnexpectedToken(
+// XdagSubscribeParams.class,
+// p.currentToken(),
+// p,
+// "eth_subscribe can only have one object to configure subscription"
+// );
+// }
+// return params;
+// }
+//}
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsVisitor.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsVisitor.java
index fc211d9f..a05f33cb 100644
--- a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsVisitor.java
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscribeParamsVisitor.java
@@ -3,17 +3,17 @@
//import io.netty.channel.Channel;
//
//public interface XdagSubscribeParamsVisitor {
-// /**
-// * @param params new heads subscription request parameters.
-// * @param channel a Netty channel to subscribe notifications to.
-// * @return a subscription id which should be used as an unsubscribe parameter.
-// */
-// SubscriptionId visit(EthSubscribeNewHeadsParams params, Channel channel);
-//
-// /**
-// * @param params logs subscription request parameters.
-// * @param channel a Netty channel to subscribe notifications to.
-// * @return a subscription id which should be used as an unsubscribe parameter.
-// */
-// SubscriptionId visit(EthSubscribeLogsParams params, Channel channel);
+//// /**
+//// * @param params new heads subscription request parameters.
+//// * @param channel a Netty channel to subscribe notifications to.
+//// * @return a subscription id which should be used as an unsubscribe parameter.
+//// */
+//// SubscriptionId visit(XdagSubscribeNewHeadsParams params, Channel channel);
+////
+//// /**
+//// * @param params logs subscription request parameters.
+//// * @param channel a Netty channel to subscribe notifications to.
+//// * @return a subscription id which should be used as an unsubscribe parameter.
+//// */
+//// SubscriptionId visit(XdagSubscribeLogsParams params, Channel channel);
//}
diff --git a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscriptionNotificationDTO.java b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscriptionNotificationDTO.java
index 170ed7bb..3dc72b22 100644
--- a/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscriptionNotificationDTO.java
+++ b/src/main/java/io/xdag/rpc/modules/xdag/subscribe/XdagSubscriptionNotificationDTO.java
@@ -1,4 +1,4 @@
-package io.xdag.rpc.modules.xdag.subscribe;
-
-public interface XdagSubscriptionNotificationDTO {
-}
+//package io.xdag.rpc.modules.xdag.subscribe;
+//
+//public interface XdagSubscriptionNotificationDTO {
+//}
diff --git a/src/main/java/io/xdag/rpc/netty/Web3WebSocketServer.java b/src/main/java/io/xdag/rpc/netty/Web3WebSocketServer.java
index edd36d28..7a90b54e 100644
--- a/src/main/java/io/xdag/rpc/netty/Web3WebSocketServer.java
+++ b/src/main/java/io/xdag/rpc/netty/Web3WebSocketServer.java
@@ -1,97 +1,97 @@
-///*
-// * This file is part of RskJ
-// * Copyright (C) 2018 RSK Labs Ltd.
-// *
-// * This program is free software: you can redistribute it and/or modify
-// * it under the terms of the GNU Lesser General Public License as published by
-// * the Free Software Foundation, either version 3 of the License, or
-// * (at your option) any later version.
-// *
-// * This program is distributed in the hope that it will be useful,
-// * but WITHOUT ANY WARRANTY; without even the implied warranty of
-// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// * GNU Lesser General Public License for more details.
-// *
-// * You should have received a copy of the GNU Lesser General Public License
-// * along with this program. If not, see .
-// */
-//package io.xdag.rpc.netty;
-//
-//import io.netty.bootstrap.ServerBootstrap;
-//import io.netty.channel.ChannelFuture;
-//import io.netty.channel.ChannelInitializer;
-//import io.netty.channel.ChannelPipeline;
-//import io.netty.channel.EventLoopGroup;
-//import io.netty.channel.nio.NioEventLoopGroup;
-//import io.netty.channel.socket.SocketChannel;
-//import io.netty.channel.socket.nio.NioServerSocketChannel;
-//import io.netty.handler.codec.http.HttpObjectAggregator;
-//import io.netty.handler.codec.http.HttpServerCodec;
-//import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//
-//import javax.annotation.Nullable;
-//import java.net.InetAddress;
-//
-//public class Web3WebSocketServer {
-// private static final Logger logger = LoggerFactory.getLogger(Web3WebSocketServer.class);
-//
-// private final InetAddress host;
-// private final int port;
-// private final XdagJsonRpcHandler jsonRpcHandler;
-// private final JsonRpcWeb3ServerHandler web3ServerHandler;
-// private final EventLoopGroup bossGroup;
-// private final EventLoopGroup workerGroup;
-// private @Nullable ChannelFuture webSocketChannel;
-//
-// public Web3WebSocketServer(
-// InetAddress host,
-// int port,
-// XdagJsonRpcHandler jsonRpcHandler,
-// JsonRpcWeb3ServerHandler web3ServerHandler) {
-// this.host = host;
-// this.port = port;
-// this.jsonRpcHandler = jsonRpcHandler;
-// this.web3ServerHandler = web3ServerHandler;
-// this.bossGroup = new NioEventLoopGroup();
-// this.workerGroup = new NioEventLoopGroup();
-// }
-//
-// public void start() {
-// logger.info("RPC WebSocket enabled");
-// ServerBootstrap b = new ServerBootstrap();
-// b.group(bossGroup, workerGroup)
-// .channel(NioServerSocketChannel.class)
-// .childHandler(new ChannelInitializer() {
-// @Override
-// protected void initChannel(SocketChannel ch) throws Exception {
-// ChannelPipeline p = ch.pipeline();
-// p.addLast(new HttpServerCodec());
-// p.addLast(new HttpObjectAggregator(1024 * 1024 * 5));
-// p.addLast(new WebSocketServerProtocolHandler("/websocket"));
-// p.addLast(jsonRpcHandler);
-// p.addLast(web3ServerHandler);
-// p.addLast(new Web3ResultWebSocketResponseHandler());
-// }
-// });
-// webSocketChannel = b.bind(host, port);
-// try {
-// webSocketChannel.sync();
-// } catch (InterruptedException e) {
-// logger.error("The RPC WebSocket server couldn't be started", e);
-// Thread.currentThread().interrupt();
-// }
-// }
-//
-// public void stop() {
-// try {
-// webSocketChannel.channel().close().sync();
-// } catch (InterruptedException e) {
-// logger.error("Couldn't stop the RPC WebSocket server", e);
-// Thread.currentThread().interrupt();
-// }
-// this.bossGroup.shutdownGracefully();
-// this.workerGroup.shutdownGracefully();
-// }
-//}
+/*
+ * This file is part of RskJ
+ * Copyright (C) 2018 RSK Labs Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package io.xdag.rpc.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.net.InetAddress;
+
+public class Web3WebSocketServer {
+ private static final Logger logger = LoggerFactory.getLogger(Web3WebSocketServer.class);
+
+ private final InetAddress host;
+ private final int port;
+ private final XdagJsonRpcHandler jsonRpcHandler;
+ private final JsonRpcWeb3ServerHandler web3ServerHandler;
+ private final EventLoopGroup bossGroup;
+ private final EventLoopGroup workerGroup;
+ private @Nullable ChannelFuture webSocketChannel;
+
+ public Web3WebSocketServer(
+ InetAddress host,
+ int port,
+ XdagJsonRpcHandler jsonRpcHandler,
+ JsonRpcWeb3ServerHandler web3ServerHandler) {
+ this.host = host;
+ this.port = port;
+ this.jsonRpcHandler = jsonRpcHandler;
+ this.web3ServerHandler = web3ServerHandler;
+ this.bossGroup = new NioEventLoopGroup();
+ this.workerGroup = new NioEventLoopGroup();
+ }
+
+ public void start() {
+ logger.info("RPC WebSocket enabled");
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline p = ch.pipeline();
+ p.addLast(new HttpServerCodec());
+ p.addLast(new HttpObjectAggregator(1024 * 1024 * 5));
+ p.addLast(new WebSocketServerProtocolHandler("/websocket"));
+ p.addLast(jsonRpcHandler);
+ p.addLast(web3ServerHandler);
+ p.addLast(new Web3ResultWebSocketResponseHandler());
+ }
+ });
+ webSocketChannel = b.bind(host, port);
+ try {
+ webSocketChannel.sync();
+ } catch (InterruptedException e) {
+ logger.error("The RPC WebSocket server couldn't be started", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public void stop() {
+ try {
+ webSocketChannel.channel().close().sync();
+ } catch (InterruptedException e) {
+ logger.error("Couldn't stop the RPC WebSocket server", e);
+ Thread.currentThread().interrupt();
+ }
+ this.bossGroup.shutdownGracefully();
+ this.workerGroup.shutdownGracefully();
+ }
+}
diff --git a/src/main/java/io/xdag/rpc/netty/XdagJsonRpcHandler.java b/src/main/java/io/xdag/rpc/netty/XdagJsonRpcHandler.java
index f077d8a9..a904bfb9 100644
--- a/src/main/java/io/xdag/rpc/netty/XdagJsonRpcHandler.java
+++ b/src/main/java/io/xdag/rpc/netty/XdagJsonRpcHandler.java
@@ -1,74 +1,70 @@
-//package io.xdag.rpc.netty;
-//
-//import io.netty.buffer.ByteBufHolder;
-//import io.netty.buffer.ByteBufInputStream;
-//import io.netty.channel.ChannelHandlerContext;
-//import io.netty.channel.SimpleChannelInboundHandler;
-//import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
-//import io.xdag.rpc.jsonrpc.JsonRpcBooleanResult;
-//import io.xdag.rpc.jsonrpc.JsonRpcIdentifiableMessage;
-//import io.xdag.rpc.jsonrpc.JsonRpcResultOrError;
-//import io.xdag.rpc.modules.XdagJsonRpcRequest;
-//import io.xdag.rpc.modules.XdagJsonRpcRequestVisitor;
-//import io.xdag.rpc.modules.eth.subscribe.EthSubscribeRequest;
-//import io.xdag.rpc.modules.eth.subscribe.EthUnsubscribeRequest;
-//import io.xdag.rpc.serialize.JsonRpcSerializer;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//
-//import java.io.IOException;
-//
-//public class XdagJsonRpcHandler extends SimpleChannelInboundHandler
-// implements XdagJsonRpcRequestVisitor {
-// private static final Logger LOGGER = LoggerFactory.getLogger(XdagJsonRpcHandler.class);
-//
-//// private final EthSubscriptionNotificationEmitter emitter;
-// private final JsonRpcSerializer serializer;
-//
-// public XdagJsonRpcHandler(JsonRpcSerializer serializer) {
+package io.xdag.rpc.netty;
+
+import io.netty.buffer.ByteBufHolder;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.xdag.rpc.jsonrpc.JsonRpcIdentifiableMessage;
+import io.xdag.rpc.jsonrpc.JsonRpcResultOrError;
+import io.xdag.rpc.modules.XdagJsonRpcRequest;
+import io.xdag.rpc.modules.XdagJsonRpcRequestVisitor;
+import io.xdag.rpc.serialize.JsonRpcSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class XdagJsonRpcHandler extends SimpleChannelInboundHandler
+ implements XdagJsonRpcRequestVisitor {
+ private static final Logger LOGGER = LoggerFactory.getLogger(XdagJsonRpcHandler.class);
+
+ private final JsonRpcSerializer serializer;
+
+ public XdagJsonRpcHandler(JsonRpcSerializer serializer) {
+ this.serializer = serializer;
+ }
+// public XdagJsonRpcHandler(EthSubscriptionNotificationEmitter emitter, JsonRpcSerializer serializer) {
+// this.emitter = emitter;
// this.serializer = serializer;
// }
-//// public XdagJsonRpcHandler(EthSubscriptionNotificationEmitter emitter, JsonRpcSerializer serializer) {
-//// this.emitter = emitter;
-//// this.serializer = serializer;
-//// }
-//
-// @Override
-// protected void channelRead0(ChannelHandlerContext ctx, ByteBufHolder msg) {
-// try {
-// XdagJsonRpcRequest request = serializer.deserializeRequest(
-// new ByteBufInputStream(msg.copy().content())
-// );
-//
-// // TODO(mc) we should support the ModuleDescription method filters
-// JsonRpcResultOrError resultOrError = request.accept(this, ctx);
-// JsonRpcIdentifiableMessage response = resultOrError.responseFor(request.getId());
-// ctx.writeAndFlush(new TextWebSocketFrame(serializer.serializeMessage(response)));
-// return;
-// } catch (IOException e) {
-// LOGGER.trace("Not a known or valid JsonRpcRequest", e);
-// }
-//
-// // delegate to the next handler if the message can't be matched to a known JSON-RPC request
-// ctx.fireChannelRead(msg.retain());
-// }
-//
-// @Override
-// public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-//// emitter.unsubscribe(ctx.channel());
-// super.channelInactive(ctx);
-// }
-//
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, ByteBufHolder msg) {
+ try {
+ XdagJsonRpcRequest request = serializer.deserializeRequest(
+ new ByteBufInputStream(msg.copy().content())
+ );
+
+ // TODO(mc) we should support the ModuleDescription method filters
+ JsonRpcResultOrError resultOrError = request.accept(this, ctx);
+ JsonRpcIdentifiableMessage response = resultOrError.responseFor(request.getId());
+ ctx.writeAndFlush(new TextWebSocketFrame(serializer.serializeMessage(response)));
+ return;
+ } catch (IOException e) {
+ LOGGER.trace("Not a known or valid JsonRpcRequest", e);
+ }
+
+ // delegate to the next handler if the message can't be matched to a known JSON-RPC request
+ ctx.fireChannelRead(msg.retain());
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+// emitter.unsubscribe(ctx.channel());
+ super.channelInactive(ctx);
+ }
+
// @Override
-// public JsonRpcResultOrError visit(EthUnsubscribeRequest request, ChannelHandlerContext ctx) {
+// public JsonRpcResultOrError visit(XDAGUnsubscribeRequest request, ChannelHandlerContext ctx) {
//// boolean unsubscribed = emitter.unsubscribe(request.getParams().getSubscriptionId());
//// return new JsonRpcBooleanResult(unsubscribed);
// return null;
// }
//
// @Override
-// public JsonRpcResultOrError visit(EthSubscribeRequest request, ChannelHandlerContext ctx) {
+// public JsonRpcResultOrError visit(XDAGSubscribeRequest request, ChannelHandlerContext ctx) {
//// return request.getParams().accept(emitter, ctx.channel());
// return null;
// }
-//}
+}
diff --git a/src/main/java/io/xdag/utils/BasicUtils.java b/src/main/java/io/xdag/utils/BasicUtils.java
index 0526e488..cf17758c 100644
--- a/src/main/java/io/xdag/utils/BasicUtils.java
+++ b/src/main/java/io/xdag/utils/BasicUtils.java
@@ -23,6 +23,7 @@
*/
package io.xdag.utils;
+import cn.hutool.core.codec.Base64;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
@@ -54,60 +55,73 @@ public static BigInteger getDiffByHash(byte[] hash) {
return ans;
}
+// public static String hash2Address(byte[] hash) {
+// hash = Arrays.reverse(hash);
+// char[] newcha = new char[32];
+// int c = 0;
+// int d;
+// int h;
+// for (int i = d = h = 0; i < 32; i++) {
+// if (d < 6) {
+// d += 8;
+// c <<= 8;
+// c |= Byte.toUnsignedInt(hash[h]);
+// h++;
+// }
+// d -= 6;
+// int index = (c >> d) & 0x3f;
+// newcha[i] = bit2mime[index];
+// }
+// return String.copyValueOf(newcha);
+// }
+
public static String hash2Address(byte[] hash) {
hash = Arrays.reverse(hash);
- char[] newcha = new char[32];
- int c = 0;
- int d;
- int h;
- for (int i = d = h = 0; i < 32; i++) {
- if (d < 6) {
- d += 8;
- c <<= 8;
- c |= Byte.toUnsignedInt(hash[h]);
- h++;
- }
- d -= 6;
- int index = (c >> d) & 0x3f;
- newcha[i] = bit2mime[index];
- }
- return String.copyValueOf(newcha);
+ byte[] addr = new byte[24];
+ System.arraycopy(hash,0,addr,0,24);
+ return Base64.encode(addr);
}
-
- /**
- * 返回低192bit的hash hashlow
- *
- * @param address
- * 256bit hash
- * @return 前64位置0的hashlow
- */
public static byte[] address2Hash(String address) {
- byte[] hashlow = new byte[32];
- int i, c, d, n;
- int h = 0, j = 0;
- for (int e = n = i = 0; i < 32; ++i) {
- do {
- if ((c = Byte.toUnsignedInt((byte) address.charAt(h++))) == 0) {
- return null;
- }
- d = Byte.toUnsignedInt(mime2bits[c]);
- } while ((d & 0xC0) != 0);
- e <<= 6;
- e |= d;
- n += 6;
-
- if (n >= 8) {
- n -= 8;
- hashlow[j++] = (byte) (e >> n);
- }
- }
-
- for (i = 0; i < 8; i++) {
- hashlow[j++] = 0;
- }
- return Arrays.reverse(hashlow);
+ byte[] ret = Base64.decode(address);
+ byte[] res = new byte[32];
+ System.arraycopy(Arrays.reverse(ret),0,res,8,24);
+ return res;
}
+// /**
+// * 返回低192bit的hash hashlow
+// *
+// * @param address
+// * 256bit hash
+// * @return 前64位置0的hashlow
+// */
+// public static byte[] address2Hash(String address) {
+// byte[] hashlow = new byte[32];
+// int i, c, d, n;
+// int h = 0, j = 0;
+// for (int e = n = i = 0; i < 32; ++i) {
+// do {
+// if ((c = Byte.toUnsignedInt((byte) address.charAt(h++))) == 0) {
+// return null;
+// }
+// d = Byte.toUnsignedInt(mime2bits[c]);
+// } while ((d & 0xC0) != 0);
+// e <<= 6;
+// e |= d;
+// n += 6;
+//
+// if (n >= 8) {
+// n -= 8;
+// hashlow[j++] = (byte) (e >> n);
+// }
+// }
+//
+// for (i = 0; i < 8; i++) {
+// hashlow[j++] = 0;
+// }
+// return Arrays.reverse(hashlow);
+// }
+
// convert xdag to cheato
public static long xdag2amount(double input) {
double amount = Math.floor(input);
diff --git a/src/main/resources/rpc_modules.conf b/src/main/resources/rpc_modules.conf
new file mode 100644
index 00000000..4b91b3f9
--- /dev/null
+++ b/src/main/resources/rpc_modules.conf
@@ -0,0 +1,96 @@
+rpc {
+ providers {
+ web {
+ cors = "localhost"
+ http {
+ enabled = true
+ bind_address = localhost
+ hosts = []
+ port = 4444
+ # A value greater than zero sets the socket value in milliseconds. Node attempts to gently close all
+ # TCP/IP connections with proper half close semantics, so a linger timeout should not be required and
+ # thus the default is -1.
+ linger_time = -1
+ }
+ ws {
+ enabled = true
+ bind_address = localhost
+ port = 4445
+ }
+ }
+ }
+
+ # Enabled RPC Modules. If the module is NOT in the list, and mark as "enabled", the rpc calls will be discard.
+ # It is possible to enable/disable a particular method in a module
+ # {
+ # name: "evm",
+ # version: "1.0",
+ # enabled: "true",
+ # methods: {
+ # enabled: [ "evm_snapshot", "evm_revert" ],
+ # disabled: [ "evm_reset", "evm_increaseTime" ]
+ # }
+ # }
+ modules = [
+ {
+ name: "xdag",
+ version: "1.0",
+ enabled: "true",
+ },
+; {
+; name: "eth",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "net",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "rpc",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "web3",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "evm",
+; version: "1.0",
+; enabled: "true"
+; },
+; {
+; name: "sco",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "txpool",
+; version: "1.0",
+; enabled: "true",
+; },
+; {
+; name: "personal",
+; version: "1.0",
+; enabled: "true"
+; },
+; {
+; name: "debug",
+; version: "1.0",
+; enabled: "true"
+; },
+; {
+; name: "trace",
+; version: "1.0",
+; enabled: "true"
+; },
+; {
+; name: "rsk",
+; version: "1.0",
+; enabled: "true"
+; }
+ ]
+}
diff --git a/src/main/resources/xdag-testnet.config b/src/main/resources/xdag-testnet.config
index bba68628..dcb29f9e 100644
--- a/src/main/resources/xdag-testnet.config
+++ b/src/main/resources/xdag-testnet.config
@@ -32,3 +32,61 @@ maxMinerPerAccount = 256
password = root
whiteIPs =
+
+modules = [
+ {
+ name: "eth",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "net",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "rpc",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "web3",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "evm",
+ version: "1.0",
+ enabled: "true"
+ },
+ {
+ name: "sco",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "txpool",
+ version: "1.0",
+ enabled: "true",
+ },
+ {
+ name: "personal",
+ version: "1.0",
+ enabled: "true"
+ },
+ {
+ name: "debug",
+ version: "1.0",
+ enabled: "true"
+ },
+ {
+ name: "trace",
+ version: "1.0",
+ enabled: "true"
+ },
+ {
+ name: "rsk",
+ version: "1.0",
+ enabled: "true"
+ }
+ ]
diff --git a/src/test/java/io/xdag/core/BlockchainTest.java b/src/test/java/io/xdag/core/BlockchainTest.java
index 3da4dfba..a21dd1a2 100644
--- a/src/test/java/io/xdag/core/BlockchainTest.java
+++ b/src/test/java/io/xdag/core/BlockchainTest.java
@@ -70,6 +70,9 @@ public class BlockchainTest {
Kernel kernel;
DatabaseFactory dbFactory;
+ BigInteger private_1 = new BigInteger("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", 16);
+ BigInteger private_2 = new BigInteger("10a55f0c18c46873ddbf9f15eddfc06f10953c601fd144474131199e04148046", 16);
+
@Before
public void setUp() throws Exception {
config.getNodeSpec().setStoreDir(root.newFolder().getAbsolutePath());
@@ -125,7 +128,7 @@ public void startCheckMain() {
@Test
public void testAddressBlock() {
- ECKeyPair key = Keys.createEcKeyPair();
+ ECKeyPair key = ECKeyPair.create(private_1);
Block addressBlock = generateAddressBlock(config, key, new Date().getTime());
MockBlockchain blockchain = new MockBlockchain(kernel);
ImportResult result = blockchain.tryToConnect(addressBlock);
@@ -142,7 +145,7 @@ public void testAddressBlock() {
public void testExtraBlock() throws ParseException {
// Date date = fastDateFormat.parse("2020-09-20 23:45:00");
long generateTime = 1600616700000L;
- ECKeyPair key = Keys.createEcKeyPair();
+ ECKeyPair key = ECKeyPair.create(private_1);
MockBlockchain blockchain = new MockBlockchain(kernel);
XdagTopStatus stats = blockchain.getXdagTopStatus();
assertNotNull(stats);
@@ -188,8 +191,8 @@ public void testExtraBlock() throws ParseException {
@Test
public void testTransactionBlock() throws ParseException {
- ECKeyPair addrKey = Keys.createEcKeyPair();
- ECKeyPair poolKey = Keys.createEcKeyPair();
+ ECKeyPair addrKey = ECKeyPair.create(private_1);
+ ECKeyPair poolKey = ECKeyPair.create(private_2);
// Date date = fastDateFormat.parse("2020-09-20 23:45:00");
long generateTime = 1600616700000L;
// 1. add one address block
@@ -315,8 +318,8 @@ public void testTransactionBlock() throws ParseException {
public void testCanUseInput() throws ParseException {
// Date date = fastDateFormat.parse("2020-09-20 23:45:00");
long generateTime = 1600616700000L;
- ECKeyPair fromKey = Keys.createEcKeyPair();
- ECKeyPair toKey = Keys.createEcKeyPair();
+ ECKeyPair fromKey = ECKeyPair.create(private_1);
+ ECKeyPair toKey = ECKeyPair.create(private_2);
Block fromAddrBlock = generateAddressBlock(config, fromKey, generateTime);
Block toAddrBlock = generateAddressBlock(config, toKey, generateTime);
@@ -420,11 +423,11 @@ public void testOriginFork() throws ParseException {
BigInteger privateKey = new BigInteger(privString, 16);
- String firstDiff = "aedbac91e1";
- String secondDiff = "d936e19b89";
+ String firstDiff = "60b6a7744b";
+ String secondDiff = "b20217d6e2";
- ECKeyPair addrKey = ECKeyPair.create(privateKey);
- ECKeyPair poolKey = ECKeyPair.create(privateKey);
+ ECKeyPair addrKey = ECKeyPair.create(private_1);
+ ECKeyPair poolKey = ECKeyPair.create(private_2);
long generateTime = 1600616700000L;
// 1. add one address block
Block addressBlock = generateAddressBlock(config, addrKey,generateTime);
diff --git a/src/test/java/io/xdag/core/ExtraBlockTest.java b/src/test/java/io/xdag/core/ExtraBlockTest.java
index 7f701e57..b6c1afa6 100644
--- a/src/test/java/io/xdag/core/ExtraBlockTest.java
+++ b/src/test/java/io/xdag/core/ExtraBlockTest.java
@@ -68,6 +68,9 @@ public class ExtraBlockTest {
long expectedExtraBlocks = 5;
+ BigInteger private_1 = new BigInteger("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", 16);
+ BigInteger private_2 = new BigInteger("10a55f0c18c46873ddbf9f15eddfc06f10953c601fd144474131199e04148046", 16);
+
@Before
public void setUp() throws Exception {
config.getNodeSpec().setStoreDir(root.newFolder().getAbsolutePath());
@@ -124,8 +127,8 @@ public void testExtraBlockReUse() throws ParseException {
String privString = "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4";
BigInteger privateKey = new BigInteger(privString, 16);
- ECKeyPair addrKey = ECKeyPair.create(privateKey);
- ECKeyPair poolKey = ECKeyPair.create(privateKey);
+ ECKeyPair addrKey = ECKeyPair.create(private_1);
+ ECKeyPair poolKey = ECKeyPair.create(private_2);
// Date date = fastDateFormat.parse("2020-09-20 23:45:00");
long generateTime = 1600616700000L;
// 1. add one address block
diff --git a/src/test/java/io/xdag/core/RandomXSyncTest.java b/src/test/java/io/xdag/core/RandomXSyncTest.java
index 81653d9e..11d40a6f 100644
--- a/src/test/java/io/xdag/core/RandomXSyncTest.java
+++ b/src/test/java/io/xdag/core/RandomXSyncTest.java
@@ -66,7 +66,7 @@ public class RandomXSyncTest {
Config config = new DevnetConfig();
public static FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
- private final String privString = "421d725da10c056a955d2444a5a043b1a5d4515db126b8631806a8ccbda93369";
+ private final String privString = "10a55f0c18c46873ddbf9f15eddfc06f10953c601fd144474131199e04148046";
private final BigInteger privateKey = new BigInteger(privString, 16);
private long forkHeight;
@@ -76,7 +76,7 @@ public void init() {
RandomXConstants.SEEDHASH_EPOCH_TESTNET_BLOCKS = 64;
RandomXConstants.RANDOMX_TESTNET_FORK_HEIGHT = 128;
RandomXConstants.SEEDHASH_EPOCH_TESTNET_LAG = 4;
- forkHeight = 2;
+ forkHeight = 3;
}
diff --git a/src/test/java/io/xdag/core/RewardTest.java b/src/test/java/io/xdag/core/RewardTest.java
index 4a1df999..a43386ca 100644
--- a/src/test/java/io/xdag/core/RewardTest.java
+++ b/src/test/java/io/xdag/core/RewardTest.java
@@ -66,6 +66,10 @@ public class RewardTest {
Kernel kernel;
DatabaseFactory dbFactory;
+ String privString = "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4";
+ BigInteger privateKey = new BigInteger(privString, 16);
+
+
class MockBlockchain extends BlockchainImpl {
public MockBlockchain(Kernel kernel) {
@@ -110,8 +114,6 @@ public void setUp() throws Exception {
@Test
public void testReward() throws ParseException {
- String privString = "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4";
- BigInteger privateKey = new BigInteger(privString, 16);
RandomXConstants.RANDOMX_TESTNET_FORK_HEIGHT = 16000;
RandomXConstants.SEEDHASH_EPOCH_TESTNET_BLOCKS = 16;
diff --git a/src/test/java/io/xdag/rpc/jsonrpc/JsonRpcResultResponseTest.java b/src/test/java/io/xdag/rpc/jsonrpc/JsonRpcResultResponseTest.java
new file mode 100644
index 00000000..9c31b5e4
--- /dev/null
+++ b/src/test/java/io/xdag/rpc/jsonrpc/JsonRpcResultResponseTest.java
@@ -0,0 +1,24 @@
+package io.xdag.rpc.jsonrpc;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class JsonRpcResultResponseTest {
+ private ObjectMapper serializer = new ObjectMapper();
+
+ @Test
+ public void serializeResponseWithResult() throws IOException {
+ String message = "{\"jsonrpc\":\"2.0\",\"id\":48,\"result\":true}";
+ assertEquals(
+ serializer.writeValueAsString(
+ new JsonRpcResultResponse(48, new JsonRpcBooleanResult(true))
+ ),
+ message
+ );
+ }
+}
From 3266442348b68c52c782f83fa290b2131b8a2de4 Mon Sep 17 00:00:00 2001
From: punk8 <1401501690@qq.com>
Date: Tue, 25 May 2021 11:50:13 +0800
Subject: [PATCH 03/11] fix sign problem
---
src/test/java/io/xdag/BlockBuilder.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/test/java/io/xdag/BlockBuilder.java b/src/test/java/io/xdag/BlockBuilder.java
index 5a3a2212..8f3b58c1 100644
--- a/src/test/java/io/xdag/BlockBuilder.java
+++ b/src/test/java/io/xdag/BlockBuilder.java
@@ -46,6 +46,7 @@ public static Block generateAddressBlock(Config config, ECKeyPair key, long xdag
return b;
}
+ // TODO:set nonce means this block is a mining block, the mining param need to set true
public static Block generateExtraBlock(Config config, ECKeyPair key, long xdagTime, List pendings) {
Block b = new Block(config, xdagTime, null, pendings, true, null, null, -1);
b.signOut(key);
@@ -54,6 +55,7 @@ public static Block generateExtraBlock(Config config, ECKeyPair key, long xdagTi
return b;
}
+ // TODO:set nonce means this block is a mining block, the mining param need to set true
public static Block generateExtraBlockGivenRandom(Config config, ECKeyPair key, long xdagTime, List pendings, String randomS) {
Block b = new Block(config, xdagTime, null, pendings, true, null, null, -1);
b.signOut(key);
From 2db2dbd69f52e2c3525966945b3b601f4432ec7b Mon Sep 17 00:00:00 2001
From: punk8 <1401501690@qq.com>
Date: Tue, 25 May 2021 21:59:57 +0800
Subject: [PATCH 04/11] merge mnemonic-wallet
---
README.md | 63 ++-
pom.xml | 19 +
script/xdag.sh | 17 +
src/main/java/io/xdag/Bootstrap.java | 53 +-
src/main/java/io/xdag/Kernel.java | 16 +-
src/main/java/io/xdag/Launcher.java | 69 ++-
src/main/java/io/xdag/cli/Commands.java | 16 +-
src/main/java/io/xdag/cli/XdagCli.java | 486 +++++++++++++++++
.../XdagOption.java} | 39 +-
.../java/io/xdag/config/AbstractConfig.java | 4 +-
src/main/java/io/xdag/config/Constants.java | 4 +
.../java/io/xdag/config/DevnetConfig.java | 2 +
.../java/io/xdag/config/MainnetConfig.java | 2 +
.../java/io/xdag/config/TestnetConfig.java | 2 +
.../java/io/xdag/config/spec/WalletSpec.java | 1 +
src/main/java/io/xdag/consensus/XdagPow.java | 4 +-
.../java/io/xdag/core/BlockchainImpl.java | 10 +-
src/main/java/io/xdag/core/SimpleEncoder.java | 95 +++-
src/main/java/io/xdag/crypto/Aes.java | 102 ++++
src/main/java/io/xdag/crypto/Credentials.java | 87 ---
src/main/java/io/xdag/crypto/Keys.java | 79 +--
.../xdag/mine/manager/AwardManagerImpl.java | 18 +-
.../java/io/xdag/utils/ByteArrayWrapper.java | 4 +
.../java/io/xdag/utils/SimpleDecoder.java | 155 ++++++
src/main/java/io/xdag/utils/SystemUtil.java | 10 +
src/main/java/io/xdag/wallet/Bip39Wallet.java | 58 --
.../java/io/xdag/wallet/Bip44WalletUtils.java | 80 ---
src/main/java/io/xdag/wallet/Wallet.java | 516 ++++++++++++------
src/main/java/io/xdag/wallet/WalletFile.java | 467 ----------------
.../java/io/xdag/wallet/WalletManager.java | 73 ---
src/main/java/io/xdag/wallet/WalletUtils.java | 215 +-------
src/test/java/io/xdag/cli/XdagCliTest.java | 464 ++++++++++++++++
.../java/io/xdag/config/DevnetConfigTest.java | 23 +
.../io/xdag/config/MainnetConfigTest.java | 23 +
.../io/xdag/config/TestnetConfigTest.java | 23 +
.../java/io/xdag/core/BlockchainTest.java | 26 +-
.../java/io/xdag/core/ExtraBlockTest.java | 26 +-
.../java/io/xdag/core/RandomXSyncTest.java | 15 +-
src/test/java/io/xdag/core/RewardTest.java | 29 +-
.../java/io/xdag/crypto/CredentialsTest.java | 51 --
.../java/io/xdag/crypto/ECRecoverTest.java | 87 ---
src/test/java/io/xdag/crypto/KeysTest.java | 59 --
src/test/java/io/xdag/crypto/SampleKeys.java | 4 +-
.../io/xdag/mine/miner/MinerConnectTest.java | 30 +-
.../io/xdag/wallet/Bip44WalletUtilsTest.java | 109 ----
.../java/io/xdag/wallet/WalletFileTest.java | 87 ---
.../io/xdag/wallet/WalletManagerTest.java | 66 ---
src/test/java/io/xdag/wallet/WalletTest.java | 193 +++++--
.../java/io/xdag/wallet/WalletUtilsTest.java | 245 +++------
49 files changed, 2286 insertions(+), 2040 deletions(-)
create mode 100755 script/xdag.sh
create mode 100644 src/main/java/io/xdag/cli/XdagCli.java
rename src/main/java/io/xdag/{wallet/CipherException.java => cli/XdagOption.java} (70%)
create mode 100644 src/main/java/io/xdag/crypto/Aes.java
delete mode 100644 src/main/java/io/xdag/crypto/Credentials.java
create mode 100644 src/main/java/io/xdag/utils/SimpleDecoder.java
delete mode 100644 src/main/java/io/xdag/wallet/Bip39Wallet.java
delete mode 100644 src/main/java/io/xdag/wallet/Bip44WalletUtils.java
delete mode 100644 src/main/java/io/xdag/wallet/WalletFile.java
delete mode 100644 src/main/java/io/xdag/wallet/WalletManager.java
create mode 100644 src/test/java/io/xdag/cli/XdagCliTest.java
delete mode 100644 src/test/java/io/xdag/crypto/CredentialsTest.java
delete mode 100644 src/test/java/io/xdag/crypto/ECRecoverTest.java
delete mode 100644 src/test/java/io/xdag/wallet/Bip44WalletUtilsTest.java
delete mode 100644 src/test/java/io/xdag/wallet/WalletFileTest.java
delete mode 100644 src/test/java/io/xdag/wallet/WalletManagerTest.java
diff --git a/README.md b/README.md
index 40e7a6b3..f3399f18 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@
- [System environment](#system-environment)
- [Installation and usage](#installation-and-usage)
- [Develop](#develop)
+ - [XDAG Mars Project](#XDAG-Mars-Project)
- [Code](#code)
- [Contribution](#contribution)
- [Sponsorship](#sponsorship)
@@ -35,21 +36,59 @@ The Private Chain Building Tutorial helps you to build a private-chain for testi
XDAGJ already has the basic functions as a pool, and the follow-up work is to improve the stability of the system while optimizing the existing code. It is important to adopt excellent blockchain technology for XDAGJ.
-The main work of the next stage includes but not limited
+### XDAG Mars Project
-- Optimize the XDAG consensus process and synchronization protocol
-- LibP2P is used to replace the DNET network and gradually improve the degree of decentralization as long as the system stability allows.
-- Open API interface, provide black box testing.
-- Add a commission to the transaction.
-- Use BIPxx and other specifications to improve the existing public and private key and address generation methods, and provide a more convenient and universal key storage method.
-- Add snapshot function to solve the problem of slow loading speed of historical main block.
-- Optimize the address block structure to avoid dust attacks.
+The XDAG Mars Project is divided into four stages:
-At the same time, we are also actively providing more application scenarios for XDAG, including but not limited.
+#### Exploration phase: XDAGJ testnet is online (online, in beta)
+
+- [x] Deploy XDAGJ test network environment, open beta
+
+- [x] Implement the RandomX algorithm
+
+- [x] Implement the libp2p network protocol
+
+- [x] Testnet blockchain browser
+
+- [x] Test coin acquisition
+
+
+#### Landing phase: XDAGJ mainnet is online
+
+- [ ] Improve test cases: write test cases for existing functions
+
+- [ ] Improve the log function: provide a relatively complete log service to facilitate trouble shooting
+
+- [ ] Optimize synchronization protocol: ameliorate the existing synchronization protocol and improve synchronization efficiency
+
+- [ ] Implement the snapshot function: solve the problem of long time-consuming loading data caused by too many historical blocks
+
+- [ ] Implement the RPC function: access to Web3j, realize the standardization of the interface
+
+- [ ] Improve the mining protocol: introduce the more mature Stratum protocol
+
+- [ ] Lightweight wallet application: connect to MateMask, join the browser wallet
+
+- [ ] Standardize the format of public and private keys, follow the BIPXX specification, and add mnemonic words to generate public and private key pairs
+
+#### Expansion phase: XDAGJ & EVM
+
+- [ ] Improve the address block structure and increase the handling fee
+
+- [ ] Optimize and improve mobile wallets to improve user experience
+
+- [ ] Support smart contracts, implement EVM that supports Solidity, and be compatible with Ethereum smart contracts
+
+- [ ] Lower the threshold of mining pool users and gradually open the whitelist to achieve complete decentralization
+
+#### Prosperity phase: XDAGJ & DeFi
+
+- [ ] Implement cross-chain protocols, compatible with access to multiple blockchain systems, to achieve intercommunication between XDAG and other chain worlds
+
+- [ ] Implement the oracle function
+
+- [ ] Join a distributed exchange
-- Explore the possibility of using neo4j as the storage layer to provide a visual DAG storage.
-- Explore a way to add a virtual machine to XDAG to implement smart contracts and improve the usability of the system.
-- Explore effective cross-chain solutions to break the closed ecology of XDAG.
## Code
diff --git a/pom.xml b/pom.xml
index 3bae316f..9ad25efd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -288,6 +288,12 @@
2.8.0
+
+ commons-cli
+ commons-cli
+ 1.4
+
+
com.google.guava
guava
@@ -449,6 +455,19 @@
+
+ com.github.stefanbirkner
+ system-rules
+ 1.19.0
+ test
+
+
+ junit
+ junit-dep
+
+
+
+
org.assertj
assertj-core
diff --git a/script/xdag.sh b/script/xdag.sh
new file mode 100755
index 00000000..0a935ac0
--- /dev/null
+++ b/script/xdag.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+XDAG_VERSION="0.4.2"
+XDAG_JARNAME="xdagj-${XDAG_VERSION}-shaded.jar"
+XDAG_OPTS="-t"
+
+# Linux Java Home
+JAVA_HOME="/usr/local/java/"
+
+# MacOS Java Home
+#JAVA_HOME=/usr/local/opt/openjdk/libexec/openjdk.jdk/Contents/Home/
+
+# default JVM options
+JAVA_OPTS="--enable-preview -server -Xms1g -Xmx1g"
+
+# start kernel
+java ${JAVA_OPTS} -cp ${XDAG_JARNAME} io.xdag.Bootstrap "$@"
\ No newline at end of file
diff --git a/src/main/java/io/xdag/Bootstrap.java b/src/main/java/io/xdag/Bootstrap.java
index be578bd5..d1aa5d82 100644
--- a/src/main/java/io/xdag/Bootstrap.java
+++ b/src/main/java/io/xdag/Bootstrap.java
@@ -23,59 +23,12 @@
*/
package io.xdag;
-import io.xdag.config.*;
-import io.xdag.wallet.OldWallet;
-import lombok.extern.slf4j.Slf4j;
+import io.xdag.cli.XdagCli;
-@Slf4j
public class Bootstrap {
- public static Config getConfig(String[] args) throws Exception {
-// if (args == null || args.length == 0) {
-// throw new RuntimeException("getConfig(args) args is empty! ");
-// }
- Config config = new MainnetConfig();
- for (String arg : args) {
- switch (arg) {
- case "-t":
- config = new TestnetConfig();
- break;
- }
- }
- config.changePara(args);
- config.setDir();
- //logPoolInfo(oldConfig);
-
- // init keys
- config.initKeys();
- return config;
- }
-
- public static void logPoolInfo(Config config) {
- log.info(
- "矿池节点地址 :[{}:{}], 矿池服务地址:[{}:{}],相关配置信息:miner[{}],maxip[{}],maxconn[{}],fee[{}],reward[{}],direct[{}],fun[{}]",
- config.getNodeSpec().getNodeIp(),
- config.getNodeSpec().getNodePort(),
- config.getPoolSpec().getPoolIp(),
- config.getPoolSpec().getPoolPort(),
- config.getPoolSpec().getGlobalMinerLimit(),
- config.getPoolSpec().getMaxConnectPerIp(),
- config.getPoolSpec().getMaxMinerPerAccount(),
- config.getPoolSpec().getPoolRation(),
- config.getPoolSpec().getRewardRation(),
- config.getPoolSpec().getDirectRation(),
- config.getPoolSpec().getFundRation());
- }
-
public static void main(String[] args) throws Exception {
- Config config = getConfig(args);
-
-
- // if dnet_keys.dat and wallet.dat exist
- OldWallet wallet = new OldWallet();
-
- Kernel kernel = new Kernel(config, wallet);
- // default start kernel
- kernel.testStart();
+ XdagCli.main(args);
}
+
}
diff --git a/src/main/java/io/xdag/Kernel.java b/src/main/java/io/xdag/Kernel.java
index bfb7e1d1..4b5ff070 100644
--- a/src/main/java/io/xdag/Kernel.java
+++ b/src/main/java/io/xdag/Kernel.java
@@ -71,7 +71,7 @@
import io.xdag.rpc.serialize.JacksonBasedRpcSerializer;
import io.xdag.rpc.serialize.JsonRpcSerializer;
import io.xdag.utils.XdagTime;
-import io.xdag.wallet.OldWallet;
+import io.xdag.wallet.Wallet;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -88,7 +88,7 @@
public class Kernel {
protected Status status = Status.STOPPED;
protected Config config;
- protected OldWallet wallet;
+ protected Wallet wallet;
protected DatabaseFactory dbFactory;
protected BlockStore blockStore;
protected OrphanPool orphanPool;
@@ -136,7 +136,7 @@ public class Kernel {
protected JsonRpcWeb3FilterHandler jsonRpcWeb3FilterHandler;
protected JacksonBasedRpcSerializer jacksonBasedRpcSerializer;
- public Kernel(Config config, OldWallet wallet) {
+ public Kernel(Config config, Wallet wallet) {
this.config = config;
this.wallet = wallet;
// 启动的时候就是在初始化
@@ -171,11 +171,13 @@ public synchronized void testStart() throws Exception {
// ====================================
// wallet init
// ====================================
+
// if (wallet == null) {
- wallet = new OldWallet();
- wallet.init(this.config);
+// wallet = new OldWallet();
+// wallet.init(this.config);
+
log.info("Wallet init.");
-// }
+
dbFactory = new RocksdbFactory(this.config);
blockStore = new BlockStore(
@@ -210,7 +212,7 @@ public synchronized void testStart() throws Exception {
// 如果是第一次启动,则新建第一个地址块
if (xdagStats.getOurLastBlockHash() == null) {
firstAccount = new Block(config, XdagTime.getCurrentTimestamp(), null, null, false, null,null, -1);
- firstAccount.signOut(wallet.getDefKey().ecKey);
+ firstAccount.signOut(wallet.getDefKey());
poolMiner = new Miner(firstAccount.getHash());
xdagStats.setOurLastBlockHash(firstAccount.getHashLow());
if(xdagStats.getGlobalMiner() == null) {
diff --git a/src/main/java/io/xdag/Launcher.java b/src/main/java/io/xdag/Launcher.java
index 30a0646c..a8b09375 100644
--- a/src/main/java/io/xdag/Launcher.java
+++ b/src/main/java/io/xdag/Launcher.java
@@ -23,17 +23,27 @@
*/
package io.xdag;
+import io.xdag.config.Config;
+import io.xdag.config.MainnetConfig;
+import io.xdag.config.TestnetConfig;
+import lombok.Getter;
+import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.cli.*;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
@Slf4j
+@Getter
+@Setter
public class Launcher {
+ private final Options options = new Options();
+ private String password = null;
+ private Config config;
+
/**
* Here we make sure that all shutdown hooks will be executed in the order of
* registration. This is necessary to be manually maintained because
@@ -68,4 +78,57 @@ private static synchronized void shutdownHook() {
LogManager.shutdown();
}
+ /**
+ * Adds a supported option.
+ */
+ protected void addOption(Option option) {
+ options.addOption(option);
+ }
+
+ /**
+ * Parses options from the given arguments.
+ */
+ protected CommandLine parseOptions(String[] args) throws ParseException {
+ if (args == null || args.length == 0 ) {
+ return null;
+ }
+ CommandLineParser parser = new DefaultParser();
+ CommandLine cmd = parser.parse(getOptions(), args);
+
+ return cmd;
+ }
+
+ protected Config buildConfig(String[] args) throws Exception {
+ Config config = new MainnetConfig();
+ for (String arg : args) {
+ switch (arg) {
+ case "-t" -> config = new TestnetConfig();
+ }
+ }
+ config.changePara(args);
+ config.setDir();
+ logPoolInfo(config);
+
+ // init keys
+ config.initKeys();
+
+ return config;
+ }
+
+ public static void logPoolInfo(Config config) {
+ log.info(
+ "Xdag Node IP Address:[{}:{}], Xdag Pool Service IP Address:[{}:{}],Configure:miner[{}],maxip[{}],maxconn[{}],fee[{}],reward[{}],direct[{}],fun[{}]",
+ config.getNodeSpec().getNodeIp(),
+ config.getNodeSpec().getNodePort(),
+ config.getPoolSpec().getPoolIp(),
+ config.getPoolSpec().getPoolPort(),
+ config.getPoolSpec().getGlobalMinerLimit(),
+ config.getPoolSpec().getMaxConnectPerIp(),
+ config.getPoolSpec().getMaxMinerPerAccount(),
+ config.getPoolSpec().getPoolRation(),
+ config.getPoolSpec().getRewardRation(),
+ config.getPoolSpec().getDirectRation(),
+ config.getPoolSpec().getFundRation());
+ }
+
}
diff --git a/src/main/java/io/xdag/cli/Commands.java b/src/main/java/io/xdag/cli/Commands.java
index 0ddcf440..efa042c7 100644
--- a/src/main/java/io/xdag/cli/Commands.java
+++ b/src/main/java/io/xdag/cli/Commands.java
@@ -164,13 +164,13 @@ public String xfer(double sendAmount, byte[] address, String remark) {
int index = pair.getKey();
Block block = pair.getValue();
if (remain.get() <= block.getInfo().getAmount()) {
- ourBlocks.put(new Address(block.getHashLow(), XDAG_FIELD_IN, remain.get()), kernel.getWallet().getKeyByIndex(index));
+ ourBlocks.put(new Address(block.getHashLow(), XDAG_FIELD_IN, remain.get()), kernel.getWallet().getAccounts().get(index));
remain.set(0);
return true;
} else {
if (block.getInfo().getAmount() > 0) {
remain.set(remain.get() - block.getInfo().getAmount());
- ourBlocks.put(new Address(block.getHashLow(), XDAG_FIELD_IN, block.getInfo().getAmount()), kernel.getWallet().getKeyByIndex(index));
+ ourBlocks.put(new Address(block.getHashLow(), XDAG_FIELD_IN, block.getInfo().getAmount()), kernel.getWallet().getAccounts().get(index));
return false;
}
return false;
@@ -213,7 +213,7 @@ private List createTransactionBlock(Map ourKey
// 保证key的唯一性
Set keysPerBlock = new HashSet<>();
// 放入defkey
- keysPerBlock.add(kernel.getWallet().getDefKey().ecKey);
+ keysPerBlock.add(kernel.getWallet().getDefKey());
// base count a block
int base = 1 + 1 + 2 + hasRemark;
@@ -239,7 +239,7 @@ private List createTransactionBlock(Map ourKey
// 清空keys,准备下一个
keys = new HashMap<>();
keysPerBlock = new HashSet<>();
- keysPerBlock.add(kernel.getWallet().getDefKey().ecKey);
+ keysPerBlock.add(kernel.getWallet().getDefKey());
base = 1 + 1 + 2 + hasRemark;
amount = 0;
}
@@ -262,7 +262,7 @@ private BlockWrapper createTransaction(byte[] to, long amount, Map argsList = new ArrayList<>();
+ for (String arg : args) {
+ switch (arg) {
+ case "-t" -> config = new TestnetConfig();
+ default -> argsList.add(arg);
+ }
+ }
+ String[] newArgs = argsList.toArray(new String[0]);
+ // parse common options
+ CommandLine cmd = null;
+ try {
+ cmd = parseOptions(newArgs);
+ } catch (ParseException exception) {
+ System.err.println("Parsing Failed:" + exception.getMessage());
+ }
+
+ if(cmd == null) {
+ start();
+ } else if (cmd.hasOption(XdagOption.HELP.toString())) {
+ printHelp();
+ } else if (cmd.hasOption(XdagOption.VERSION.toString())) {
+ printVersion();
+ } else if (cmd.hasOption(XdagOption.ACCOUNT.toString())) {
+ String action = cmd.getOptionValue(XdagOption.ACCOUNT.toString()).trim();
+ switch (action) {
+ case "init" -> initHDAccount();
+ case "create" -> createAccount();
+ case "list" -> listAccounts();
+ default -> System.out.println("No Action!");
+ }
+ } else if (cmd.hasOption(XdagOption.CHANGE_PASSWORD.toString())) {
+ changePassword();
+ } else if (cmd.hasOption(XdagOption.DUMP_PRIVATE_KEY.toString())) {
+ dumpPrivateKey(cmd.getOptionValue(XdagOption.DUMP_PRIVATE_KEY.toString()).trim());
+ } else if (cmd.hasOption(XdagOption.IMPORT_PRIVATE_KEY.toString())) {
+ importPrivateKey(cmd.getOptionValue(XdagOption.IMPORT_PRIVATE_KEY.toString()).trim());
+ } else if (cmd.hasOption(XdagOption.IMPORT_MNEMONIC.toString())) {
+ importMnemonic(cmd.getOptionValue(XdagOption.IMPORT_MNEMONIC.toString()).trim());
+ } else if (cmd.hasOption(XdagOption.CONVERT_OLD_WALLET.toString())) {
+ File file = new File(cmd.getOptionValue(XdagOption.CONVERT_OLD_WALLET.toString()).trim());
+ convertOldWallet(file);
+ }
+ }
+
+ protected void printHelp() {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.setWidth(200);
+ formatter.printHelp("./xdag.sh [options]", getOptions());
+ }
+
+ protected void printVersion() {
+ System.out.println(Constants.CLIENT_VERSION);
+ }
+
+ protected void start() throws IOException {
+ // create/unlock wallet
+ Wallet wallet = loadWallet().exists() ? loadAndUnlockWallet() : createNewWallet();
+ if (wallet == null) {
+ return;
+ }
+
+ if (!wallet.isHdWalletInitialized()) {
+ initializedHdSeed(wallet, System.out);
+ }
+
+ // create a new account if the wallet is empty
+ List accounts = wallet.getAccounts();
+ if (accounts.isEmpty()) {
+ ECKeyPair key = wallet.addAccountWithNextHdKey();
+ wallet.flush();
+ System.out.println("New Address:" + BytesUtils.toHexString(Keys.toBytesAddress(key)));
+ }
+
+ // start kernel
+ try {
+ startKernel(getConfig(), wallet);
+ } catch (Exception e) {
+ System.err.println("Uncaught exception during kernel startup:" + e.getMessage());
+ exit(-1);
+ }
+ }
+
+ /**
+ * Starts the kernel.
+ */
+ protected Kernel startKernel(Config config, Wallet wallet) throws Exception {
+ Kernel kernel = new Kernel(config, wallet);
+ kernel.testStart();
+ return kernel;
+ }
+
+ protected void initHDAccount() {
+ // create/unlock wallet
+ Wallet wallet;
+ if(loadWallet().exists()) {
+ wallet = loadAndUnlockWallet();
+ } else {
+ wallet = createNewWallet();
+ }
+
+ if (wallet == null) {
+ return;
+ }
+ if (!wallet.isHdWalletInitialized()) {
+ initializedHdSeed(wallet, System.out);
+ } else {
+ System.out.println("HD Wallet Account already init.");
+ }
+ }
+
+ protected void createAccount() {
+ Wallet wallet = loadAndUnlockWallet();
+ if(Objects.nonNull(wallet) && !wallet.isHdWalletInitialized()) {
+ System.out.println("Please init HD Wallet account first!");
+ return;
+ }
+ ECKeyPair key = wallet.addAccountWithNextHdKey();
+ if (wallet.flush()) {
+ System.out.println("New Address:" + BytesUtils.toHexString(Keys.toBytesAddress(key)));
+ System.out.println("PublicKey:" + BytesUtils.toHexString(key.getPublicKey().toByteArray()));
+ }
+ }
+
+ protected void listAccounts() {
+ Wallet wallet = loadAndUnlockWallet();
+ List accounts = wallet.getAccounts();
+
+ if (accounts.isEmpty()) {
+ System.out.println("Account Missing");
+ } else {
+ for (int i = 0; i < accounts.size(); i++) {
+ System.out.println("Address:" + i + " " + BytesUtils.toHexString(Keys.toBytesAddress(accounts.get(i))));
+ }
+ }
+ }
+
+ protected void changePassword() {
+ Wallet wallet = loadAndUnlockWallet();
+ if(wallet.isUnlocked()) {
+ String newPassword = readNewPassword("EnterNewPassword", "ReEnterNewPassword");
+ if (newPassword == null) {
+ return;
+ }
+ wallet.changePassword(newPassword);
+ boolean isFlushed = wallet.flush();
+ if (!isFlushed) {
+ System.out.println("Wallet File Cannot Be Updated");
+ return;
+ }
+ System.out.println("Password Changed Successfully!");
+ }
+ }
+
+ protected void exit(int code) {
+ System.exit(code);
+ }
+
+ protected void dumpPrivateKey(String address) {
+ Wallet wallet = loadAndUnlockWallet();
+ byte[] addressBytes = BytesUtils.hexStringToBytes(address);
+ ECKeyPair account = wallet.getAccount(addressBytes);
+ if (account == null) {
+ System.out.println("Address Not In Wallet");
+ } else {
+ System.out.println("Private:" + BytesUtils.toHexString(account.getPrivateKey().toByteArray()));
+ }
+ System.out.println("Private Dump Successfully!");
+ }
+
+ protected boolean importPrivateKey(String key) {
+ Wallet wallet = loadAndUnlockWallet();
+ byte[] keyBytes = BytesUtils.hexStringToBytes(key);
+ ECKeyPair account = ECKeyPair.create(keyBytes);
+
+ boolean accountAdded = wallet.addAccount(account);
+ if (!accountAdded) {
+ System.out.println("Private Key Already In Wallet");
+ return false;
+ }
+
+ boolean walletFlushed = wallet.flush();
+ if (!walletFlushed) {
+ System.out.println("Wallet File Cannot Be Updated");
+ return false;
+ }
+
+ System.out.println("Address:" + BytesUtils.toHexString(Keys.toBytesAddress(account)));
+ System.out.println("PublicKey:" + BytesUtils.toHexString(account.getPublicKey().toByteArray()));
+ System.out.println("Private Key Imported Successfully!");
+ return true;
+ }
+
+ protected boolean importMnemonic(String mnemonic) {
+ Wallet wallet = loadAndUnlockWallet();
+
+ if(wallet.isHdWalletInitialized()) {
+ System.out.println("HDWallet Mnemonic Already In Wallet");
+ return false;
+ }
+
+ if(!MnemonicUtils.validateMnemonic(mnemonic)) {
+ System.out.println("Wrong Mnemonic");
+ return false;
+ }
+
+ // default add one hd key
+ createAccount();
+
+ if (!wallet.flush()) {
+ System.out.println("HDWallet File Cannot Be Updated");
+ return false;
+ }
+ System.out.println("HDWallet Mnemonic Imported Successfully!");
+ return true;
+ }
+
+ protected boolean convertOldWallet(File file) {
+ if(!file.exists()) {
+ System.out.println("File:" + file.getName() + " not exists.");
+ return false;
+ }
+ List keyList = readOldWallet(file);
+ for(ECKeyPair key : keyList) {
+ System.out.println("PrivateKey:" + BytesUtils.toHexString(key.getPrivateKey().toByteArray()));
+ System.out.println(" PublicKey:" + BytesUtils.toHexString(key.getPublicKey().toByteArray()));
+ System.out.println(" Address:" + BytesUtils.toHexString(Keys.toBytesAddress(key)));
+ }
+ System.out.println("Old Wallet Converted Successfully!");
+ return true;
+ }
+
+ private List readOldWallet(File walletDatFile) {
+ byte[] priv32Encrypted = new byte[32];
+ int keysNum = 0;
+ List keyList = new ArrayList<>();
+ try (FileInputStream fileInputStream = new FileInputStream(walletDatFile)) {
+ while (fileInputStream.read(priv32Encrypted) != -1) {
+ byte[] priv32 = Native.uncrypt_wallet_key(priv32Encrypted, keysNum++);
+ ECKeyPair ecKey = ECKeyPair.create(Numeric.toBigInt(priv32));
+ keyList.add(ecKey);
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return keyList;
+ }
+
+ public Wallet loadWallet() {
+ return new Wallet(getConfig());
+ }
+
+ public Wallet loadAndUnlockWallet() {
+ Wallet wallet = loadWallet();
+ if (getPassword() == null) {
+ if (wallet.unlock("")) {
+ setPassword("");
+ } else {
+ setPassword(readPassword());
+ }
+ }
+
+ if (!wallet.unlock(getPassword())) {
+ System.err.println("Invalid password");
+ }
+
+ return wallet;
+ }
+
+ /**
+ * Create a new wallet with a new password
+ */
+ public Wallet createNewWallet() {
+ String newPassword = readNewPassword("EnterNewPassword:", "ReEnterNewPassword:");
+ if (newPassword == null) {
+ return null;
+ }
+
+ setPassword(newPassword);
+ Wallet wallet = loadWallet();
+
+ if (!wallet.unlock(newPassword) || !wallet.flush()) {
+ System.err.println("Create New WalletError");
+ System.exit(-1);
+ return null;
+ }
+
+ return wallet;
+ }
+
+ /**
+ * Read a new password from input and require confirmation
+ */
+ public String readNewPassword(String newPasswordMessageKey, String reEnterNewPasswordMessageKey) {
+ String newPassword = readPassword(newPasswordMessageKey);
+ String newPasswordRe = readPassword(reEnterNewPasswordMessageKey);
+
+ if (!newPassword.equals(newPasswordRe)) {
+ System.err.println("ReEnter NewPassword Incorrect");
+ System.exit(-1);
+ return null;
+ }
+
+ return newPassword;
+ }
+
+ /**
+ * Reads a line from the console.
+ */
+ public String readLine(String prompt) {
+ if (prompt != null) {
+ System.out.print(prompt);
+ System.out.flush();
+ }
+
+ return scanner.nextLine();
+ }
+
+ public boolean initializedHdSeed(Wallet wallet, PrintStream printer) {
+ if (wallet.isUnlocked() && !wallet.isHdWalletInitialized()) {
+ // HD Mnemonic
+ printer.println("HdWallet Initializing...");
+ byte[] initialEntropy = new byte[16];
+ SecureRandomUtils.secureRandom().nextBytes(initialEntropy);
+ String phrase = MnemonicUtils.generateMnemonic(initialEntropy);
+ printer.println("HdWallet Mnemonic:"+ phrase);
+
+ String repeat = readLine("HdWallet Mnemonic Repeat:");
+ repeat = String.join(" ", repeat.trim().split("\\s+"));
+
+ if (!repeat.equals(phrase)) {
+ printer.println("HdWallet Initialized Failure");
+ return false;
+ }
+
+ wallet.initializeHdWallet(phrase);
+ wallet.flush();
+ printer.println("HdWallet Initialized Successfully!");
+ return true;
+ }
+ return false;
+ }
+
+ public String readPassword(String prompt) {
+ Console console = System.console();
+ if (console == null) {
+ if (prompt != null) {
+ System.out.print(prompt);
+ System.out.flush();
+ }
+ return scanner.nextLine();
+ }
+ return new String(console.readPassword(prompt));
+ }
+
+ public String readPassword() {
+ return readPassword("Please enter your password: ");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/xdag/wallet/CipherException.java b/src/main/java/io/xdag/cli/XdagOption.java
similarity index 70%
rename from src/main/java/io/xdag/wallet/CipherException.java
rename to src/main/java/io/xdag/cli/XdagOption.java
index 6aea0196..1fb8d0d2 100644
--- a/src/main/java/io/xdag/wallet/CipherException.java
+++ b/src/main/java/io/xdag/cli/XdagOption.java
@@ -21,20 +21,37 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package io.xdag.wallet;
+package io.xdag.cli;
-/** Cipher exception wrapper. */
-public class CipherException extends Exception {
+public enum XdagOption {
- public CipherException(String message) {
- super(message);
- }
+ HELP("help"),
+
+ VERSION("version"),
+
+ ACCOUNT("account"),
+
+ CHANGE_PASSWORD("changepassword"),
+
+ PASSWORD("password"),
+
+ DUMP_PRIVATE_KEY("dumpprivatekey"),
- public CipherException(Throwable cause) {
- super(cause);
+ IMPORT_PRIVATE_KEY("importprivatekey"),
+
+ IMPORT_MNEMONIC("importmnemonic"),
+
+ CONVERT_OLD_WALLET("convertoldwallet");
+
+ private final String name;
+
+ XdagOption(String s) {
+ name = s;
}
- public CipherException(String message, Throwable cause) {
- super(message, cause);
+ @Override
+ public String toString() {
+ return this.name;
}
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/io/xdag/config/AbstractConfig.java b/src/main/java/io/xdag/config/AbstractConfig.java
index c4878acf..40bc2192 100644
--- a/src/main/java/io/xdag/config/AbstractConfig.java
+++ b/src/main/java/io/xdag/config/AbstractConfig.java
@@ -124,7 +124,7 @@ public class AbstractConfig implements Config, AdminSpec, PoolSpec, NodeSpec, Wa
// =========================
// Wallet spec
// =========================
-
+ protected String walletFilePath;
// =========================
// Xdag spec
@@ -291,7 +291,7 @@ public void changePara(String[] args) {
this.poolTag = StringUtils.substring(args[i+1], 0, 31);
break;
default:
- System.out.println("Illegal instruction");
+ log.error("Illegal instruction");
}
}
}
diff --git a/src/main/java/io/xdag/config/Constants.java b/src/main/java/io/xdag/config/Constants.java
index a7b42781..cf25f9c3 100644
--- a/src/main/java/io/xdag/config/Constants.java
+++ b/src/main/java/io/xdag/config/Constants.java
@@ -53,4 +53,8 @@ public class Constants {
/** 每一轮的确认数是16 */
public static final int CONFIRMATIONS_COUNT = 16;
public static final int MAIN_BIG_PERIOD_LOG = 21;
+
+ public static final String WALLET_FILE_NAME = "wallet.data";
+
+ public static final String CLIENT_VERSION = "0.4.3";
}
diff --git a/src/main/java/io/xdag/config/DevnetConfig.java b/src/main/java/io/xdag/config/DevnetConfig.java
index c7aec33b..f1c921e6 100644
--- a/src/main/java/io/xdag/config/DevnetConfig.java
+++ b/src/main/java/io/xdag/config/DevnetConfig.java
@@ -43,6 +43,8 @@ public DevnetConfig() {
this.dnetKeyFile = this.rootDir+"/dnet_keys.bin";
this.walletKeyFile = this.rootDir+"/wallet-testnet.dat";
+
+ this.walletFilePath = this.rootDir + "/wallet/" + Constants.WALLET_FILE_NAME;
}
}
diff --git a/src/main/java/io/xdag/config/MainnetConfig.java b/src/main/java/io/xdag/config/MainnetConfig.java
index 219fd4f3..0fa0fe41 100644
--- a/src/main/java/io/xdag/config/MainnetConfig.java
+++ b/src/main/java/io/xdag/config/MainnetConfig.java
@@ -42,6 +42,8 @@ public MainnetConfig() {
this.dnetKeyFile = this.rootDir+"/dnet_keys.bin";
this.walletKeyFile = this.rootDir+"/wallet.dat";
+
+ this.walletFilePath = this.rootDir + "/wallet/" + Constants.WALLET_FILE_NAME;
}
}
diff --git a/src/main/java/io/xdag/config/TestnetConfig.java b/src/main/java/io/xdag/config/TestnetConfig.java
index 4dedf91f..1c1ca4d4 100644
--- a/src/main/java/io/xdag/config/TestnetConfig.java
+++ b/src/main/java/io/xdag/config/TestnetConfig.java
@@ -44,6 +44,8 @@ public TestnetConfig() {
this.dnetKeyFile = this.rootDir+"/dnet_keys.bin";
this.walletKeyFile = this.rootDir+"/wallet-testnet.dat";
+
+ this.walletFilePath = this.rootDir + "/wallet/" + Constants.WALLET_FILE_NAME;
}
}
diff --git a/src/main/java/io/xdag/config/spec/WalletSpec.java b/src/main/java/io/xdag/config/spec/WalletSpec.java
index 27414340..ce4e0eaf 100644
--- a/src/main/java/io/xdag/config/spec/WalletSpec.java
+++ b/src/main/java/io/xdag/config/spec/WalletSpec.java
@@ -28,4 +28,5 @@
*/
public interface WalletSpec {
String getWalletKeyFile();
+ String getWalletFilePath();
}
diff --git a/src/main/java/io/xdag/consensus/XdagPow.java b/src/main/java/io/xdag/consensus/XdagPow.java
index 4fd6f9a5..34f4b2e4 100644
--- a/src/main/java/io/xdag/consensus/XdagPow.java
+++ b/src/main/java/io/xdag/consensus/XdagPow.java
@@ -184,7 +184,7 @@ public Block generateRandomXBlock(long sendTime) {
taskIndex++;
Block block = blockchain.createNewBlock(null, null, true, null);
- block.signOut(kernel.getWallet().getDefKey().ecKey);
+ block.signOut(kernel.getWallet().getDefKey());
minShare = RandomUtils.nextBytes(32);
block.setNonce(minShare);
@@ -208,7 +208,7 @@ public Block generateBlock(long sendTime) {
taskIndex++;
Block block = blockchain.createNewBlock(null, null, true, null);
- block.signOut(kernel.getWallet().getDefKey().ecKey);
+ block.signOut(kernel.getWallet().getDefKey());
minShare = RandomUtils.nextBytes(32);
block.setNonce(minShare);
diff --git a/src/main/java/io/xdag/core/BlockchainImpl.java b/src/main/java/io/xdag/core/BlockchainImpl.java
index bfd1eb4a..b2c04856 100644
--- a/src/main/java/io/xdag/core/BlockchainImpl.java
+++ b/src/main/java/io/xdag/core/BlockchainImpl.java
@@ -40,7 +40,7 @@
import io.xdag.utils.BytesUtils;
import io.xdag.utils.XdagTime;
import io.xdag.wallet.KeyInternalItem;
-import io.xdag.wallet.OldWallet;
+import io.xdag.wallet.Wallet;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -79,7 +79,7 @@ public Thread newThread(@Nonnull Runnable r) {
}
};
- private final OldWallet wallet;
+ private final Wallet wallet;
private final BlockStore blockStore;
/** 非Extra orphan存放 */
private final OrphanPool orphanPool;
@@ -714,7 +714,7 @@ public Block createNewBlock(Map pairs, List to, boo
assert pairs != null;
List keys = new ArrayList<>(Set.copyOf(pairs.values()));
for (int i = 0; i < keys.size(); i++) {
- if (keys.get(i).equals(wallet.getDefKey().ecKey)) {
+ if (keys.get(i).equals(wallet.getDefKey())) {
defKeyIndex = i;
}
}
@@ -1130,12 +1130,12 @@ public boolean canUseInput(Block block) {
}
public boolean checkMineAndAdd(Block block) {
- List ourkeys = wallet.getKey_internal();
+ List ourkeys = wallet.getAccounts();
// 输出签名只有一个
ECDSASignature signature = block.getOutsig();
// 遍历所有key
for (int i = 0; i < ourkeys.size(); i++) {
- ECKeyPair ecKey = ourkeys.get(i).ecKey;
+ ECKeyPair ecKey = ourkeys.get(i);
byte[] publicKeyBytes = Sign.publicKeyBytesFromPrivate(ecKey.getPrivateKey(), true);
byte[] digest = BytesUtils.merge(
block.getSubRawData(block.getOutsigIndex() - 2), publicKeyBytes);
diff --git a/src/main/java/io/xdag/core/SimpleEncoder.java b/src/main/java/io/xdag/core/SimpleEncoder.java
index c948b038..f906d127 100644
--- a/src/main/java/io/xdag/core/SimpleEncoder.java
+++ b/src/main/java/io/xdag/core/SimpleEncoder.java
@@ -23,8 +23,11 @@
*/
package io.xdag.core;
+import io.xdag.utils.exception.SimpleCodecException;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
public class SimpleEncoder {
private final ByteArrayOutputStream out;
@@ -57,6 +60,68 @@ public void write(byte[] input) {
}
}
+ public int getWriteFieldIndex() {
+ return getWriteIndex() / 32;
+ }
+
+ public void writeBoolean(boolean b) {
+ out.write(b ? 1 : 0);
+ }
+
+ public void writeByte(byte b) {
+ out.write(b);
+ }
+
+ public void writeShort(short s) {
+ out.write(0xFF & (s >>> 8));
+ out.write(0xFF & s);
+ }
+
+ public void writeInt(int i) {
+ out.write(0xFF & (i >>> 24));
+ out.write(0xFF & (i >>> 16));
+ out.write(0xFF & (i >>> 8));
+ out.write(0xFF & i);
+ }
+
+ public void writeString(String s) {
+ writeBytes(s.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void writeLong(long l) {
+ int i1 = (int) (l >>> 32);
+ int i2 = (int) l;
+
+ writeInt(i1);
+ writeInt(i2);
+ }
+
+ /**
+ * Encode a byte array.
+ *
+ * @param bytes
+ * the byte array to encode
+ * @param vlq
+ * should always be true unless we're providing pre-mainnet support.
+ */
+ public void writeBytes(byte[] bytes, boolean vlq) {
+ if (vlq) {
+ writeSize(bytes.length);
+ } else {
+ writeInt(bytes.length);
+ }
+
+ try {
+ out.write(bytes);
+ } catch (IOException e) {
+ throw new SimpleCodecException(e);
+ }
+ }
+
+ public void writeBytes(byte[] bytes) {
+ writeBytes(bytes, true);
+ }
+
public byte[] toBytes() {
return out.toByteArray();
}
@@ -65,7 +130,33 @@ private int getWriteIndex() {
return out.size();
}
- public int getWriteFieldIndex() {
- return getWriteIndex() / 32;
+ /**
+ * Writes a size into the output byte array.
+ *
+ * @param size
+ * @throws IllegalArgumentException
+ * when the input size is negative
+ */
+ protected void writeSize(int size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("Size can't be negative: " + size);
+ } else if (size > 0x0FFFFFFF) {
+ throw new IllegalArgumentException("Size can't be larger than 0x0FFFFFFF: " + size);
+ }
+
+ int[] buf = new int[4];
+ int i = buf.length;
+ do {
+ buf[--i] = size & 0x7f;
+ size >>>= 7;
+ } while (size > 0);
+
+ while (i < buf.length) {
+ if (i != buf.length - 1) {
+ out.write((byte) (buf[i++] | 0x80));
+ } else {
+ out.write((byte) buf[i++]);
+ }
+ }
}
}
diff --git a/src/main/java/io/xdag/crypto/Aes.java b/src/main/java/io/xdag/crypto/Aes.java
new file mode 100644
index 00000000..8db3ce50
--- /dev/null
+++ b/src/main/java/io/xdag/crypto/Aes.java
@@ -0,0 +1,102 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package io.xdag.crypto;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+public class Aes {
+
+ private Aes() {
+ }
+
+ private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException {
+ // create output buffer
+ int size = cipher.getOutputSize(data.length);
+ byte[] buf = new byte[size];
+
+ // process data
+ int length1 = cipher.processBytes(data, 0, data.length, buf, 0);
+ int length2 = cipher.doFinal(buf, length1);
+ int length = length1 + length2;
+
+ // copy buffer to result, without padding
+ byte[] result = new byte[length];
+ System.arraycopy(buf, 0, result, 0, result.length);
+
+ return result;
+ }
+
+ /**
+ * Encrypt data with AES/CBC/PKCS5Padding.
+ *
+ * @param raw
+ * @param key
+ * @param iv
+ * @return
+ */
+ public static byte[] encrypt(byte[] raw, byte[] key, byte[] iv) {
+
+ try {
+ PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
+ CipherParameters params = new ParametersWithIV(new KeyParameter(key), iv);
+ aes.init(true, params);
+
+ return cipherData(aes, raw);
+ } catch (DataLengthException | IllegalArgumentException | IllegalStateException
+ | InvalidCipherTextException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ /**
+ * Decrypt data with AES/CBC/PKCS5Padding
+ *
+ * @param encrypted
+ * @param key
+ * @param iv
+ * @return
+ */
+ public static byte[] decrypt(byte[] encrypted, byte[] key, byte[] iv) {
+ try {
+ PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
+ CipherParameters params = new ParametersWithIV(new KeyParameter(key), iv);
+ aes.init(false, params);
+
+ return cipherData(aes, encrypted);
+ } catch (DataLengthException | IllegalArgumentException | IllegalStateException
+ | InvalidCipherTextException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/main/java/io/xdag/crypto/Credentials.java b/src/main/java/io/xdag/crypto/Credentials.java
deleted file mode 100644
index 0bb1a79c..00000000
--- a/src/main/java/io/xdag/crypto/Credentials.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.crypto;
-
-import io.xdag.utils.Numeric;
-
-import java.util.Objects;
-
-/** Credentials wrapper. */
-public class Credentials {
-
- private final ECKeyPair ecKeyPair;
- private final String address;
-
- private Credentials(ECKeyPair ecKeyPair, String address) {
- this.ecKeyPair = ecKeyPair;
- this.address = address;
- }
-
- public ECKeyPair getEcKeyPair() {
- return ecKeyPair;
- }
-
- public String getAddress() {
- return address;
- }
-
- public static Credentials create(ECKeyPair ecKeyPair) {
- String address = Numeric.prependHexPrefix(Keys.getAddress(ecKeyPair));
- return new Credentials(ecKeyPair, address);
- }
-
- public static Credentials create(String privateKey, String publicKey) {
- return create(new ECKeyPair(Numeric.toBigInt(privateKey), Numeric.toBigInt(publicKey)));
- }
-
- public static Credentials create(String privateKey) {
- return create(ECKeyPair.create(Numeric.toBigInt(privateKey)));
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- Credentials that = (Credentials) o;
-
- if (!Objects.equals(ecKeyPair, that.ecKeyPair)) {
- return false;
- }
-
- return Objects.equals(address, that.address);
- }
-
- @Override
- public int hashCode() {
- int result = ecKeyPair != null ? ecKeyPair.hashCode() : 0;
- result = 31 * result + (address != null ? address.hashCode() : 0);
- return result;
- }
-}
-
diff --git a/src/main/java/io/xdag/crypto/Keys.java b/src/main/java/io/xdag/crypto/Keys.java
index 8744c558..85e64e15 100644
--- a/src/main/java/io/xdag/crypto/Keys.java
+++ b/src/main/java/io/xdag/crypto/Keys.java
@@ -36,14 +36,8 @@
/** Crypto key utilities. */
public class Keys {
-
- public static final int PRIVATE_KEY_SIZE = 32;
public static final int PUBLIC_KEY_SIZE = 64;
-
- public static final int ADDRESS_SIZE = 160;
- public static final int ADDRESS_LENGTH_IN_HEX = ADDRESS_SIZE >> 2;
static final int PUBLIC_KEY_LENGTH_IN_HEX = PUBLIC_KEY_SIZE << 1;
- public static final int PRIVATE_KEY_LENGTH_IN_HEX = PRIVATE_KEY_SIZE << 1;
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -94,76 +88,13 @@ public static ECKeyPair createEcKeyPair(SecureRandom random) {
return ECKeyPair.create(keyPair);
}
- public static String getAddress(ECKeyPair ecKeyPair) {
- return getAddress(ecKeyPair.getPublicKey());
+ public static byte[] toBytesAddress(ECKeyPair key) {
+ return Hash.sha256hash160(key.getPublicKey().toByteArray());
}
- public static String getAddress(BigInteger publicKey) {
- return getAddress(
- Numeric.toHexStringWithPrefixZeroPadded(publicKey, PUBLIC_KEY_LENGTH_IN_HEX));
- }
-
- public static String getAddress(String publicKey) {
- String publicKeyNoPrefix = Numeric.cleanHexPrefix(publicKey);
-
- if (publicKeyNoPrefix.length() < PUBLIC_KEY_LENGTH_IN_HEX) {
- publicKeyNoPrefix =
- Strings.zeros(PUBLIC_KEY_LENGTH_IN_HEX - publicKeyNoPrefix.length())
- + publicKeyNoPrefix;
- }
- String hash = Hash.sha256(publicKeyNoPrefix);
- return hash.substring(hash.length() - ADDRESS_LENGTH_IN_HEX); // right most 160 bits
- }
-
- public static byte[] getAddress(byte[] publicKey) {
- byte[] hash = Hash.sha256(publicKey);
- return Arrays.copyOfRange(hash, hash.length - 20, hash.length); // right most 160 bits
- }
-
-// /**
-// * Checksum address encoding as per EIP-55.
-// *
-// * @param address a valid hex encoded address
-// * @return hex encoded checksum address
-// */
-// public static String toChecksumAddress(String address) {
-// String lowercaseAddress = Numeric.cleanHexPrefix(address).toLowerCase();
-// String addressHash = Numeric.cleanHexPrefix(Hash.sha3String(lowercaseAddress));
-//
-// StringBuilder result = new StringBuilder(lowercaseAddress.length() + 2);
-//
-// result.append("0x");
-//
-// for (int i = 0; i < lowercaseAddress.length(); i++) {
-// if (Integer.parseInt(String.valueOf(addressHash.charAt(i)), 16) >= 8) {
-// result.append(String.valueOf(lowercaseAddress.charAt(i)).toUpperCase());
-// } else {
-// result.append(lowercaseAddress.charAt(i));
-// }
-// }
-//
-// return result.toString();
-// }
-
- public static byte[] serialize(ECKeyPair ecKeyPair) {
- byte[] privateKey = Numeric.toBytesPadded(ecKeyPair.getPrivateKey(), PRIVATE_KEY_SIZE);
- byte[] publicKey = Numeric.toBytesPadded(ecKeyPair.getPublicKey(), PUBLIC_KEY_SIZE);
-
- byte[] result = Arrays.copyOf(privateKey, PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE);
- System.arraycopy(publicKey, 0, result, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE);
- return result;
- }
-
- public static ECKeyPair deserialize(byte[] input) {
- if (input.length != PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE) {
- throw new RuntimeException("Invalid input key size");
- }
-
- BigInteger privateKey = Numeric.toBigInt(input, 0, PRIVATE_KEY_SIZE);
- BigInteger publicKey = Numeric.toBigInt(input, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE);
-
- return new ECKeyPair(privateKey, publicKey);
+ public static String toBase58Address(ECKeyPair key) {
+ byte[] addrBytes = toBytesAddress(key);
+ return Base58.encode(addrBytes);
}
}
diff --git a/src/main/java/io/xdag/mine/manager/AwardManagerImpl.java b/src/main/java/io/xdag/mine/manager/AwardManagerImpl.java
index 6b8315fa..6034d212 100644
--- a/src/main/java/io/xdag/mine/manager/AwardManagerImpl.java
+++ b/src/main/java/io/xdag/mine/manager/AwardManagerImpl.java
@@ -38,12 +38,12 @@
import io.xdag.crypto.ECKeyPair;
import io.xdag.mine.MinerChannel;
import io.xdag.utils.*;
-import io.xdag.wallet.OldWallet;
import io.xdag.Kernel;
import io.xdag.consensus.Task;
import io.xdag.mine.miner.Miner;
import io.xdag.mine.miner.MinerStates;
+import io.xdag.wallet.Wallet;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
@@ -75,7 +75,7 @@ public class AwardManagerImpl implements AwardManager, Runnable {
private MinerManager minerManager;
private final Kernel kernel;
private final Blockchain blockchain;
- private final OldWallet xdagWallet;
+ private final Wallet wallet;
private ArrayList diff = new ArrayList<>();
private ArrayList prev_diff = new ArrayList<>();
@@ -84,7 +84,7 @@ public AwardManagerImpl(Kernel kernel) {
this.kernel = kernel;
this.config = kernel.getConfig();
this.blockchain = kernel.getBlockchain();
- this.xdagWallet = kernel.getWallet();
+ this.wallet = kernel.getWallet();
this.poolMiner = kernel.getPoolMiner();
this.minerManager = kernel.getMinerManager();
init();
@@ -173,7 +173,7 @@ private static double diffToPay(double sum, int diffCount) {
}
public void init() {
- log.debug("容器初始化");
+ log.debug("AwardManager init.");
// 容器的初始化
for (int i = 0; i < 16; i++) {
blockHashs.add(null);
@@ -327,7 +327,7 @@ public int payMiners(long time) {
// 决定一个区块是否需要再有一个签名字段
// todo 这里不够严谨把 如果时第三把第四把呢
- if (xdagWallet.getKey_internal().size() - 1 == keyPos) {
+ if (wallet.getAccounts().size() - 1 == keyPos) {
payminersPerBlock = 11;
} else {
payminersPerBlock = 9;
@@ -470,7 +470,7 @@ public void doPayments(byte[] hash, int paymentsPerBlock, PayData payData, int k
ArrayList receipt = new ArrayList<>(paymentsPerBlock - 1);
Map inputMap = new HashMap<>();
Address input = new Address(hash, XDAG_FIELD_IN);
- ECKeyPair inputKey = xdagWallet.getKeyByIndex(keyPos);
+ ECKeyPair inputKey = wallet.getAccount(keyPos);
inputMap.put(input, inputKey);
long payAmount = 0L;
/**
@@ -539,14 +539,14 @@ public void transaction(byte[] hashLow, ArrayList receipt, long payAmou
}
Map inputMap = new HashMap<>();
Address input = new Address(hashLow, XDAG_FIELD_IN, payAmount);
- ECKeyPair inputKey = xdagWallet.getKeyByIndex(keypos);
+ ECKeyPair inputKey = wallet.getAccount(keypos);
inputMap.put(input, inputKey);
Block block = blockchain.createNewBlock(inputMap, receipt, false, null);
- if (inputKey.equals(xdagWallet.getDefKey().ecKey)) {
+ if (inputKey.equals(wallet.getDefKey())) {
block.signOut(inputKey);
} else {
block.signIn(inputKey);
- block.signOut(xdagWallet.getDefKey().ecKey);
+ block.signOut(wallet.getDefKey());
}
log.debug("pay block hash【{}】", Hex.toHexString(block.getHash()));
diff --git a/src/main/java/io/xdag/utils/ByteArrayWrapper.java b/src/main/java/io/xdag/utils/ByteArrayWrapper.java
index 6bea389d..53edc5e4 100644
--- a/src/main/java/io/xdag/utils/ByteArrayWrapper.java
+++ b/src/main/java/io/xdag/utils/ByteArrayWrapper.java
@@ -39,6 +39,10 @@ public byte[] getData() {
return data;
}
+ public static ByteArrayWrapper of(byte[] data) {
+ return new ByteArrayWrapper(data);
+ }
+
@Override
public boolean equals(Object other) {
if (!(other instanceof ByteArrayWrapper)) {
diff --git a/src/main/java/io/xdag/utils/SimpleDecoder.java b/src/main/java/io/xdag/utils/SimpleDecoder.java
new file mode 100644
index 00000000..30157f0a
--- /dev/null
+++ b/src/main/java/io/xdag/utils/SimpleDecoder.java
@@ -0,0 +1,155 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package io.xdag.utils;
+
+import io.xdag.utils.exception.SimpleCodecException;
+
+import java.io.UnsupportedEncodingException;
+
+public class SimpleDecoder {
+ private static final String ENCODING = "UTF-8";
+
+ private final byte[] in;
+ private final int from;
+ private final int to;
+
+ private int index;
+
+ public SimpleDecoder(byte[] in) {
+ this(in, 0, in.length);
+ }
+
+ public SimpleDecoder(byte[] in, int from) {
+ this(in, from, in.length);
+ }
+
+ public SimpleDecoder(byte[] in, int from, int to) {
+ this.in = in;
+ this.from = from;
+ this.to = to;
+ this.index = from;
+ }
+
+ public boolean readBoolean() {
+ require(1);
+ return in[index++] != 0;
+ }
+
+ public byte readByte() {
+ require(1);
+ return in[index++];
+ }
+
+ public short readShort() {
+ require(2);
+ return (short) ((in[index++] & 0xFF) << 8 | (in[index++] & 0xFF));
+ }
+
+ public int readInt() {
+ require(4);
+ return in[index++] << 24 | (in[index++] & 0xFF) << 16 | (in[index++] & 0xFF) << 8 | (in[index++] & 0xFF);
+ }
+
+ public long readLong() {
+ int i1 = readInt();
+ int i2 = readInt();
+
+ return (unsignedInt(i1) << 32) | unsignedInt(i2);
+ }
+
+ /**
+ * Decode a byte array.
+ *
+ * @param vlq
+ * should always be true unless we're providing pre-mainnet support.
+ */
+ public byte[] readBytes(boolean vlq) {
+ int len = vlq ? readSize() : readInt();
+
+ require(len);
+ byte[] buf = new byte[len];
+ System.arraycopy(in, index, buf, 0, len);
+ index += len;
+
+ return buf;
+ }
+
+ public byte[] readBytes() {
+ return readBytes(true);
+ }
+
+ public String readString() {
+ try {
+ return new String(readBytes(), ENCODING);
+ } catch (UnsupportedEncodingException e) {
+ throw new SimpleCodecException(e);
+ }
+ }
+
+ public int getReadIndex() {
+ return index;
+ }
+
+ /**
+ * Reads size from the input.
+ *
+ * @return
+ */
+ protected int readSize() {
+ int size = 0;
+ for (int i = 0; i < 4; i++) {
+ require(1);
+ byte b = in[index++];
+
+ size = (size << 7) | (b & 0x7F);
+ if ((b & 0x80) == 0) {
+ break;
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Checks if the required bytes is satisfied.
+ *
+ * @param n
+ */
+ protected void require(int n) {
+ if (to - index < n) {
+ String msg = String.format("input [%d, %d], require: [%d %d]", from, to, index, index + n);
+ throw new IndexOutOfBoundsException(msg);
+ }
+ }
+
+ /**
+ * Re-interprets an integer as unsigned integer.
+ *
+ * @param i
+ * an integer
+ * @return the unsigned value, represented in long
+ */
+ protected long unsignedInt(int i) {
+ return i & 0x00000000ffffffffL;
+ }
+}
diff --git a/src/main/java/io/xdag/utils/SystemUtil.java b/src/main/java/io/xdag/utils/SystemUtil.java
index 353ab936..fc56cdbb 100644
--- a/src/main/java/io/xdag/utils/SystemUtil.java
+++ b/src/main/java/io/xdag/utils/SystemUtil.java
@@ -23,6 +23,7 @@
*/
package io.xdag.utils;
+import java.nio.file.FileSystems;
import java.util.Locale;
public class SystemUtil {
@@ -86,4 +87,13 @@ public static String getOsArch() {
return System.getProperty("os.arch");
}
+ /**
+ * Check if current OS is POSIX compliant.
+ *
+ * @return whether current OS is POSIX compliant
+ */
+ public static boolean isPosix() {
+ return FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
+ }
+
}
diff --git a/src/main/java/io/xdag/wallet/Bip39Wallet.java b/src/main/java/io/xdag/wallet/Bip39Wallet.java
deleted file mode 100644
index 2ec2ecd9..00000000
--- a/src/main/java/io/xdag/wallet/Bip39Wallet.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-/** Data class encapsulating a BIP-39 compatible Xdag wallet. */
-public class Bip39Wallet {
- /** Path to wallet file. */
- private final String filename;
-
- /** Generated BIP-39 mnemonic for the wallet. */
- private final String mnemonic;
-
- public Bip39Wallet(String filename, String mnemonic) {
- this.filename = filename;
- this.mnemonic = mnemonic;
- }
-
- public String getFilename() {
- return filename;
- }
-
- public String getMnemonic() {
- return mnemonic;
- }
-
- @Override
- public String toString() {
- return "XdagBip39Wallet{"
- + "filename='"
- + filename
- + '\''
- + ", mnemonic='"
- + mnemonic
- + '\''
- + '}';
- }
-}
diff --git a/src/main/java/io/xdag/wallet/Bip44WalletUtils.java b/src/main/java/io/xdag/wallet/Bip44WalletUtils.java
deleted file mode 100644
index d8dbc08e..00000000
--- a/src/main/java/io/xdag/wallet/Bip44WalletUtils.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import io.xdag.crypto.Bip32ECKeyPair;
-import io.xdag.crypto.Credentials;
-import io.xdag.crypto.MnemonicUtils;
-import io.xdag.crypto.SecureRandomUtils;
-
-import java.io.File;
-import java.io.IOException;
-
-import static io.xdag.crypto.Bip32ECKeyPair.HARDENED_BIT;
-
-public class Bip44WalletUtils extends WalletUtils {
- // https://github.com/satoshilabs/slips/blob/master/slip-0044.md
- public static final int XDAG_BIP44_CION_TYPE = 586;
-
- /**
- * Generates a BIP-44 compatible Ethereum wallet on top of BIP-39 generated seed.
- *
- * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed
- * @param destinationDirectory The directory containing the wallet
- * @return A BIP-39 compatible Ethereum wallet
- * @throws CipherException if the underlying cipher is not available
- * @throws IOException if the destination cannot be written to
- */
- public static Bip39Wallet generateBip44Wallet(String password, File destinationDirectory)
- throws CipherException, IOException {
- byte[] initialEntropy = new byte[16];
- SecureRandomUtils.secureRandom().nextBytes(initialEntropy);
-
- String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
-
- Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
- Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair);
-
- String walletFile = generateWalletFile(password, bip44Keypair, destinationDirectory, false);
-
- return new Bip39Wallet(walletFile, mnemonic);
- }
-
- public static Bip32ECKeyPair generateBip44KeyPair(Bip32ECKeyPair master) {
- // m/44'/586'/0'/0/0
- // xdag coin type 586 at https://github.com/satoshilabs/slips/blob/master/slip-0044.md
- final int[] path = {44 | HARDENED_BIT, XDAG_BIP44_CION_TYPE | HARDENED_BIT, 0 | HARDENED_BIT, 0, 0};
- return Bip32ECKeyPair.deriveKeyPair(master, path);
-
- }
-
- public static Credentials loadBip44Credentials(String password, String mnemonic) {
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
- Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
- Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair);
- return Credentials.create(bip44Keypair);
- }
-
-}
diff --git a/src/main/java/io/xdag/wallet/Wallet.java b/src/main/java/io/xdag/wallet/Wallet.java
index 35d38661..c6fe47d8 100644
--- a/src/main/java/io/xdag/wallet/Wallet.java
+++ b/src/main/java/io/xdag/wallet/Wallet.java
@@ -23,230 +23,428 @@
*/
package io.xdag.wallet;
-
-import io.xdag.crypto.ECKeyPair;
-import io.xdag.crypto.Hash;
-import io.xdag.crypto.Keys;
-import io.xdag.utils.Numeric;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
-import org.bouncycastle.crypto.generators.SCrypt;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.UUID;
+import io.xdag.config.Config;
+import io.xdag.core.SimpleEncoder;
+import io.xdag.crypto.*;
+import io.xdag.utils.ByteArrayWrapper;
+import io.xdag.utils.SimpleDecoder;
+import io.xdag.utils.SystemUtil;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.bouncycastle.crypto.generators.BCrypt;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.*;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static io.xdag.crypto.SecureRandomUtils.secureRandom;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
+@Slf4j
+@Getter
+@Setter
public class Wallet {
- private static final int N_LIGHT = 1 << 12;
- private static final int P_LIGHT = 6;
+ private static final int VERSION = 4;
+ private static final int SALT_LENGTH = 16;
+ private static final int BCRYPT_COST = 12;
+ private static final String MNEMONIC_PASS_PHRASE = "";
- private static final int N_STANDARD = 1 << 18;
- private static final int P_STANDARD = 1;
+ public static final Set POSIX_SECURED_PERMISSIONS = Set.of(OWNER_READ, OWNER_WRITE);
- private static final int R = 8;
- private static final int DKLEN = 32;
+ private final File file;
+ private final Config config;
- private static final int CURRENT_VERSION = 3;
+ private final Map accounts = Collections.synchronizedMap(new LinkedHashMap<>());
+ private String password;
- private static final String CIPHER = "aes-128-ctr";
- static final String AES_128_CTR = "pbkdf2";
- static final String SCRYPT = "scrypt";
+ // hd wallet key
+ private String mnemonicPhrase = "";
+ private int nextAccountIndex = 0;
- public static WalletFile create(String password, ECKeyPair ecKeyPair, int n, int p)
- throws CipherException {
+ /**
+ * Creates a new wallet instance.
+ */
+ public Wallet(Config config) {
+ this.file = FileUtils.getFile(config.getWalletSpec().getWalletFilePath());
+ this.config = config;
+ }
- byte[] salt = generateRandomBytes(32);
+ /**
+ * Returns whether the wallet file exists and non-empty.
+ */
+ public boolean exists() {
+ return file.length() > 0;
+ }
- byte[] derivedKey =
- generateDerivedScryptKey(password.getBytes(UTF_8), salt, n, R, p, DKLEN);
+ /**
+ * Deletes the wallet file.
+ */
+ public void delete() throws IOException {
+ Files.delete(file.toPath());
+ }
- byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
- byte[] iv = generateRandomBytes(16);
+ /**
+ * Returns the file where the wallet is persisted.
+ */
+ public File getFile() {
+ return file;
+ }
- byte[] privateKeyBytes =
- Numeric.toBytesPadded(ecKeyPair.getPrivateKey(), Keys.PRIVATE_KEY_SIZE);
+ /**
+ * Locks the wallet.
+ */
+ public void lock() {
+ password = null;
+ accounts.clear();
+ }
- byte[] cipherText =
- performCipherOperation(Cipher.ENCRYPT_MODE, iv, encryptKey, privateKeyBytes);
+ public ECKeyPair getDefKey() {
+ List accountList = getAccounts();
+ if(CollectionUtils.isNotEmpty(accountList)) {
+ return accountList.get(0);
+ }
+ return null;
+ }
- byte[] mac = generateMac(derivedKey, cipherText);
+ /**
+ * Unlocks this wallet
+ */
+ public boolean unlock(String password) {
+ if (password == null) {
+ throw new IllegalArgumentException("Password can not be null");
+ }
- return createWalletFile(ecKeyPair, cipherText, iv, salt, mac, n, p);
+ try {
+ byte[] key;
+ byte[] salt;
+
+ if (exists()) {
+
+ SimpleDecoder dec = new SimpleDecoder(FileUtils.readFileToByteArray(file));
+ int version = dec.readInt(); // version
+
+ Set newAccounts = null;
+ switch (version) {
+ // only version 4
+ case 4 -> {
+ salt = dec.readBytes();
+ key = BCrypt.generate(password.getBytes(UTF_8), salt, BCRYPT_COST);
+ try {
+ newAccounts = readAccounts(key, dec, true, version);
+ readHdSeed(key, dec);
+ } catch (Exception e) {
+ log.warn("Failed to read HD mnemonic phrase");
+ return false;
+ }
+ }
+ default -> throw new RuntimeException("Unknown wallet version.");
+ }
+
+ synchronized (accounts) {
+ accounts.clear();
+ for (ECKeyPair account : newAccounts) {
+ ByteArrayWrapper baw = ByteArrayWrapper.of(Keys.toBytesAddress(account));
+ accounts.put(baw, account);
+ }
+ }
+ }
+ this.password = password;
+ return true;
+ } catch (Exception e) {
+ log.error("Failed to open wallet", e);
+ }
+ return false;
}
- public static WalletFile createStandard(String password, ECKeyPair ecKeyPair)
- throws CipherException {
- return create(password, ecKeyPair, N_STANDARD, P_STANDARD);
+ /**
+ * Reads the account keys.
+ */
+ protected LinkedHashSet readAccounts(byte[] key, SimpleDecoder dec, boolean vlq, int version) {
+ LinkedHashSet keys = new LinkedHashSet<>();
+ int total = dec.readInt(); // size
+
+ for (int i = 0; i < total; i++) {
+ byte[] iv = dec.readBytes(vlq);
+ byte[] privateKey = Aes.decrypt(dec.readBytes(vlq), key, iv);
+ keys.add(ECKeyPair.create(privateKey));
+ }
+ return keys;
}
- public static WalletFile createLight(String password, ECKeyPair ecKeyPair)
- throws CipherException {
- return create(password, ecKeyPair, N_LIGHT, P_LIGHT);
+ /**
+ * Writes the account keys.
+ */
+ protected void writeAccounts(byte[] key, SimpleEncoder enc) {
+ synchronized (accounts) {
+ enc.writeInt(accounts.size());
+ for (ECKeyPair a : accounts.values()) {
+ byte[] iv = SecureRandomUtils.secureRandom().generateSeed(16);
+
+ enc.writeBytes(iv);
+ enc.writeBytes(Aes.encrypt(a.getPrivateKey().toByteArray(), key, iv));
+ }
+ }
}
- private static WalletFile createWalletFile(
- ECKeyPair ecKeyPair,
- byte[] cipherText,
- byte[] iv,
- byte[] salt,
- byte[] mac,
- int n,
- int p) {
-
- WalletFile walletFile = new WalletFile();
- walletFile.setAddress(Keys.getAddress(ecKeyPair));
+ /**
+ * Reads the mnemonic phase and next account index.
+ */
+ protected void readHdSeed(byte[] key, SimpleDecoder dec) {
+ byte[] iv = dec.readBytes();
+ byte[] hdSeedEncrypted = dec.readBytes();
+ byte[] hdSeedRaw = Aes.decrypt(hdSeedEncrypted, key, iv);
+
+ SimpleDecoder d = new SimpleDecoder(hdSeedRaw);
+ mnemonicPhrase = d.readString();
+ nextAccountIndex = d.readInt();
+ }
- WalletFile.Crypto crypto = new WalletFile.Crypto();
- crypto.setCipher(CIPHER);
- crypto.setCiphertext(Numeric.toHexStringNoPrefix(cipherText));
+ /**
+ * Writes the mnemonic phase and next account index.
+ */
+ protected void writeHdSeed(byte[] key, SimpleEncoder enc) {
+ SimpleEncoder e = new SimpleEncoder();
+ e.writeString(mnemonicPhrase);
+ e.writeInt(nextAccountIndex);
- WalletFile.CipherParams cipherParams = new WalletFile.CipherParams();
- cipherParams.setIv(Numeric.toHexStringNoPrefix(iv));
- crypto.setCipherparams(cipherParams);
+ byte[] iv = SecureRandomUtils.secureRandom().generateSeed(16);
+ byte[] hdSeedRaw = e.toBytes();
+ byte[] hdSeedEncrypted = Aes.encrypt(hdSeedRaw, key, iv);
- crypto.setKdf(SCRYPT);
- WalletFile.ScryptKdfParams kdfParams = new WalletFile.ScryptKdfParams();
- kdfParams.setDklen(DKLEN);
- kdfParams.setN(n);
- kdfParams.setP(p);
- kdfParams.setR(R);
- kdfParams.setSalt(Numeric.toHexStringNoPrefix(salt));
- crypto.setKdfparams(kdfParams);
+ enc.writeBytes(iv);
+ enc.writeBytes(hdSeedEncrypted);
+ }
- crypto.setMac(Numeric.toHexStringNoPrefix(mac));
- walletFile.setCrypto(crypto);
- walletFile.setId(UUID.randomUUID().toString());
- walletFile.setVersion(CURRENT_VERSION);
+ /**
+ * Returns if this wallet is unlocked.
+ */
+ public boolean isUnlocked() {
+ return !isLocked();
+ }
- return walletFile;
+ /**
+ * Returns whether the wallet is locked.
+ */
+ public boolean isLocked() {
+ return password == null;
}
- private static byte[] generateDerivedScryptKey(
- byte[] password, byte[] salt, int n, int r, int p, int dkLen) {
- return SCrypt.generate(password, salt, n, r, p, dkLen);
+ /**
+ * Sets the accounts inside this wallet.
+ */
+ public void setAccounts(List list) {
+ requireUnlocked();
+ accounts.clear();
+ for (ECKeyPair key : list) {
+ addAccount(key);
+ }
}
- private static byte[] generateAes128CtrDerivedKey(
- byte[] password, byte[] salt, int c, String prf) throws CipherException {
+ /**
+ * Returns a copy of the accounts inside this wallet.
+ */
+ public List getAccounts(){
+ requireUnlocked();
+ synchronized (accounts) {
+ return new ArrayList<>(accounts.values());
+ }
+ }
- if (!prf.equals("hmac-sha256")) {
- throw new CipherException("Unsupported prf:" + prf);
+ /**
+ * Returns account by index.
+ */
+ public ECKeyPair getAccount(int idx) {
+ requireUnlocked();
+ synchronized (accounts) {
+ return getAccounts().get(idx);
}
+ }
- // Java 8 supports this, but you have to convert the password to a character array, see
- // http://stackoverflow.com/a/27928435/3211687
+ /**
+ * Returns account by address.
+ */
+ public ECKeyPair getAccount(byte[] address) {
+ requireUnlocked();
- PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
- gen.init(password, salt, c);
- return ((KeyParameter) gen.generateDerivedParameters(256)).getKey();
+ synchronized (accounts) {
+ return accounts.get(ByteArrayWrapper.of(address));
+ }
}
- private static byte[] performCipherOperation(
- int mode, byte[] iv, byte[] encryptKey, byte[] text) throws CipherException {
+ /**
+ * Flushes this wallet into the disk.
+ */
+ public boolean flush() {
+ requireUnlocked();
try {
- IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+ SimpleEncoder enc = new SimpleEncoder();
+ enc.writeInt(VERSION);
- SecretKeySpec secretKeySpec = new SecretKeySpec(encryptKey, "AES");
- cipher.init(mode, secretKeySpec, ivParameterSpec);
- return cipher.doFinal(text);
- } catch (NoSuchPaddingException
- | NoSuchAlgorithmException
- | InvalidAlgorithmParameterException
- | InvalidKeyException
- | BadPaddingException
- | IllegalBlockSizeException e) {
- throw new CipherException("Error performing cipher operation", e);
+ byte[] salt = SecureRandomUtils.secureRandom().generateSeed(SALT_LENGTH);
+ enc.writeBytes(salt);
+
+ byte[] key = BCrypt.generate(password.getBytes(UTF_8), salt, BCRYPT_COST);
+
+ writeAccounts(key, enc);
+ writeHdSeed(key, enc);
+
+ if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
+ log.error("Failed to create the directory for wallet");
+ return false;
+ }
+
+ // set posix permissions
+ if (SystemUtil.isPosix() && !file.exists()) {
+ Files.createFile(file.toPath());
+ Files.setPosixFilePermissions(file.toPath(), POSIX_SECURED_PERMISSIONS);
+ }
+
+ FileUtils.writeByteArrayToFile(file, enc.toBytes());
+ return true;
+ } catch (IOException e) {
+ log.error("Failed to write wallet to disk", e);
}
+ return false;
}
- private static byte[] generateMac(byte[] derivedKey, byte[] cipherText) {
- byte[] result = new byte[16 + cipherText.length];
-
- System.arraycopy(derivedKey, 16, result, 0, 16);
- System.arraycopy(cipherText, 0, result, 16, cipherText.length);
- return Hash.sha256(result);
+ private void requireUnlocked() {
+ if (!isUnlocked()) {
+ throw new RuntimeException("Wallet is Locked!");
+ }
}
- public static ECKeyPair decrypt(String password, WalletFile walletFile) throws CipherException {
+ /**
+ * Adds a new account to the wallet.
+ */
+ public boolean addAccount(ECKeyPair newKey) {
+ requireUnlocked();
- validate(walletFile);
+ synchronized (accounts) {
+ ByteArrayWrapper address = ByteArrayWrapper.of(Keys.toBytesAddress(newKey));
+ if (accounts.containsKey(address)) {
+ return false;
+ }
- WalletFile.Crypto crypto = walletFile.getCrypto();
+ accounts.put(address, newKey);
+ return true;
+ }
+ }
+
+ /**
+ * Add an account with randomly generated key.
+ */
+ public ECKeyPair addAccountRandom() {
+ ECKeyPair key = Keys.createEcKeyPair();
+ addAccount(key);
+ return key;
+ }
- byte[] mac = Numeric.hexStringToByteArray(crypto.getMac());
- byte[] iv = Numeric.hexStringToByteArray(crypto.getCipherparams().getIv());
- byte[] cipherText = Numeric.hexStringToByteArray(crypto.getCiphertext());
+ /**
+ * Adds a list of accounts to the wallet.
+ */
+ public int addAccounts(List accounts) {
+ requireUnlocked();
- byte[] derivedKey;
+ int n = 0;
+ for (ECKeyPair acc : accounts) {
+ n += addAccount(acc) ? 1 : 0;
+ }
+ return n;
+ }
- WalletFile.KdfParams kdfParams = crypto.getKdfparams();
- if (kdfParams instanceof WalletFile.ScryptKdfParams) {
- WalletFile.ScryptKdfParams scryptKdfParams =
- (WalletFile.ScryptKdfParams) crypto.getKdfparams();
- int dklen = scryptKdfParams.getDklen();
- int n = scryptKdfParams.getN();
- int p = scryptKdfParams.getP();
- int r = scryptKdfParams.getR();
- byte[] salt = Numeric.hexStringToByteArray(scryptKdfParams.getSalt());
- derivedKey = generateDerivedScryptKey(password.getBytes(UTF_8), salt, n, r, p, dklen);
- } else if (kdfParams instanceof WalletFile.Aes128CtrKdfParams) {
- WalletFile.Aes128CtrKdfParams aes128CtrKdfParams =
- (WalletFile.Aes128CtrKdfParams) crypto.getKdfparams();
- int c = aes128CtrKdfParams.getC();
- String prf = aes128CtrKdfParams.getPrf();
- byte[] salt = Numeric.hexStringToByteArray(aes128CtrKdfParams.getSalt());
+ /**
+ * Deletes an account in the wallet.
+ */
+ public boolean removeAccount(ECKeyPair key) {
+ return removeAccount(Keys.toBytesAddress(key));
+ }
- derivedKey = generateAes128CtrDerivedKey(password.getBytes(UTF_8), salt, c, prf);
- } else {
- throw new CipherException("Unable to deserialize params: " + crypto.getKdf());
+ /**
+ * Deletes an account in the wallet.
+ */
+ public boolean removeAccount(byte[] address) {
+ requireUnlocked();
+ synchronized (accounts) {
+ return accounts.remove(ByteArrayWrapper.of(address)) != null;
}
+ }
- byte[] derivedMac = generateMac(derivedKey, cipherText);
+ /**
+ * Changes the password of the wallet.
+ */
+ public void changePassword(String newPassword) {
+ requireUnlocked();
- if (!Arrays.equals(derivedMac, mac)) {
- throw new CipherException("Invalid password provided");
+ if (newPassword == null) {
+ throw new IllegalArgumentException("Password can not be null");
}
- byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
- byte[] privateKey = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText);
- return ECKeyPair.create(privateKey);
+ this.password = newPassword;
}
- static void validate(WalletFile walletFile) throws CipherException {
- WalletFile.Crypto crypto = walletFile.getCrypto();
+ // ================
+ // HD wallet
+ // ================
+
+ /**
+ * Returns whether the HD seed is initialized.
+ *
+ * @return true if set, otherwise false
+ */
+ public boolean isHdWalletInitialized() {
+ requireUnlocked();
+ return mnemonicPhrase != null && !mnemonicPhrase.isEmpty();
+ }
- if (walletFile.getVersion() != CURRENT_VERSION) {
- throw new CipherException("Wallet version is not supported");
- }
+ /**
+ * Initialize the HD wallet.
+ *
+ * @param mnemonicPhrase
+ * the mnemonic word list
+ */
+ public void initializeHdWallet(String mnemonicPhrase) {
+ this.mnemonicPhrase = mnemonicPhrase;
+ this.nextAccountIndex = 0;
+ }
- if (!crypto.getCipher().equals(CIPHER)) {
- throw new CipherException("Wallet cipher is not supported");
- }
+ /**
+ * Returns the HD seed.
+ */
+ public byte[] getSeed() {
+ return MnemonicUtils.generateSeed(this.mnemonicPhrase, MNEMONIC_PASS_PHRASE);
+ }
- if (!crypto.getKdf().equals(AES_128_CTR) && !crypto.getKdf().equals(SCRYPT)) {
- throw new CipherException("KDF type is not supported");
+ /**
+ * Derives a key based on the current HD account index, and put it into the
+ * wallet.
+ */
+ public ECKeyPair addAccountWithNextHdKey() {
+ requireUnlocked();
+ requireHdWalletInitialized();
+
+ synchronized (accounts) {
+ byte[] seed = getSeed();
+ Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
+ Bip32ECKeyPair bip44Keypair = WalletUtils.generateBip44KeyPair(masterKeypair, nextAccountIndex++);
+ ByteArrayWrapper address = ByteArrayWrapper.of(Keys.toBytesAddress(bip44Keypair));
+ accounts.put(address, bip44Keypair);
+ return bip44Keypair;
}
}
- static byte[] generateRandomBytes(int size) {
- byte[] bytes = new byte[size];
- secureRandom().nextBytes(bytes);
- return bytes;
+ private void requireHdWalletInitialized() {
+ if (!isHdWalletInitialized()) {
+ throw new IllegalArgumentException("HD Seed is not initialized");
+ }
}
+
}
diff --git a/src/main/java/io/xdag/wallet/WalletFile.java b/src/main/java/io/xdag/wallet/WalletFile.java
deleted file mode 100644
index aa56e578..00000000
--- a/src/main/java/io/xdag/wallet/WalletFile.java
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import com.fasterxml.jackson.annotation.JsonSetter;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import java.io.IOException;
-
-public class WalletFile {
-
- private String address;
- private Crypto crypto;
- private String id;
- private int version;
-
- public WalletFile() {}
-
- public String getAddress() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address = address;
- }
-
- public Crypto getCrypto() {
- return crypto;
- }
-
- @JsonSetter("crypto")
- public void setCrypto(Crypto crypto) {
- this.crypto = crypto;
- }
-
- @JsonSetter("Crypto") // older wallet files may have this attribute name
- public void setCryptoV1(Crypto crypto) {
- setCrypto(crypto);
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public int getVersion() {
- return version;
- }
-
- public void setVersion(int version) {
- this.version = version;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof WalletFile)) {
- return false;
- }
-
- WalletFile that = (WalletFile) o;
-
- if (getAddress() != null
- ? !getAddress().equals(that.getAddress())
- : that.getAddress() != null) {
- return false;
- }
- if (getCrypto() != null
- ? !getCrypto().equals(that.getCrypto())
- : that.getCrypto() != null) {
- return false;
- }
- if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) {
- return false;
- }
- return version == that.version;
- }
-
- @Override
- public int hashCode() {
- int result = getAddress() != null ? getAddress().hashCode() : 0;
- result = 31 * result + (getCrypto() != null ? getCrypto().hashCode() : 0);
- result = 31 * result + (getId() != null ? getId().hashCode() : 0);
- result = 31 * result + version;
- return result;
- }
-
- public static class Crypto {
- private String cipher;
- private String ciphertext;
- private CipherParams cipherparams;
-
- private String kdf;
- private KdfParams kdfparams;
-
- private String mac;
-
- public Crypto() {}
-
- public String getCipher() {
- return cipher;
- }
-
- public void setCipher(String cipher) {
- this.cipher = cipher;
- }
-
- public String getCiphertext() {
- return ciphertext;
- }
-
- public void setCiphertext(String ciphertext) {
- this.ciphertext = ciphertext;
- }
-
- public CipherParams getCipherparams() {
- return cipherparams;
- }
-
- public void setCipherparams(CipherParams cipherparams) {
- this.cipherparams = cipherparams;
- }
-
- public String getKdf() {
- return kdf;
- }
-
- public void setKdf(String kdf) {
- this.kdf = kdf;
- }
-
- public KdfParams getKdfparams() {
- return kdfparams;
- }
-
- @JsonTypeInfo(
- use = JsonTypeInfo.Id.NAME,
- include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
- property = "kdf")
- @JsonSubTypes({
- @JsonSubTypes.Type(value = Aes128CtrKdfParams.class, name = Wallet.AES_128_CTR),
- @JsonSubTypes.Type(value = ScryptKdfParams.class, name = Wallet.SCRYPT)
- })
- // To support my Ether Wallet keys uncomment this annotation & comment out the above
- // @JsonDeserialize(using = KdfParamsDeserialiser.class)
- // Also add the following to the ObjectMapperFactory
- // objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
- public void setKdfparams(KdfParams kdfparams) {
- this.kdfparams = kdfparams;
- }
-
- public String getMac() {
- return mac;
- }
-
- public void setMac(String mac) {
- this.mac = mac;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof Crypto)) {
- return false;
- }
-
- Crypto that = (Crypto) o;
-
- if (getCipher() != null
- ? !getCipher().equals(that.getCipher())
- : that.getCipher() != null) {
- return false;
- }
- if (getCiphertext() != null
- ? !getCiphertext().equals(that.getCiphertext())
- : that.getCiphertext() != null) {
- return false;
- }
- if (getCipherparams() != null
- ? !getCipherparams().equals(that.getCipherparams())
- : that.getCipherparams() != null) {
- return false;
- }
- if (getKdf() != null ? !getKdf().equals(that.getKdf()) : that.getKdf() != null) {
- return false;
- }
- if (getKdfparams() != null
- ? !getKdfparams().equals(that.getKdfparams())
- : that.getKdfparams() != null) {
- return false;
- }
- return getMac() != null ? getMac().equals(that.getMac()) : that.getMac() == null;
- }
-
- @Override
- public int hashCode() {
- int result = getCipher() != null ? getCipher().hashCode() : 0;
- result = 31 * result + (getCiphertext() != null ? getCiphertext().hashCode() : 0);
- result = 31 * result + (getCipherparams() != null ? getCipherparams().hashCode() : 0);
- result = 31 * result + (getKdf() != null ? getKdf().hashCode() : 0);
- result = 31 * result + (getKdfparams() != null ? getKdfparams().hashCode() : 0);
- result = 31 * result + (getMac() != null ? getMac().hashCode() : 0);
- return result;
- }
- }
-
- public static class CipherParams {
- private String iv;
-
- public CipherParams() {}
-
- public String getIv() {
- return iv;
- }
-
- public void setIv(String iv) {
- this.iv = iv;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof CipherParams)) {
- return false;
- }
-
- CipherParams that = (CipherParams) o;
-
- return getIv() != null ? getIv().equals(that.getIv()) : that.getIv() == null;
- }
-
- @Override
- public int hashCode() {
- int result = getIv() != null ? getIv().hashCode() : 0;
- return result;
- }
- }
-
- interface KdfParams {
- int getDklen();
-
- String getSalt();
- }
-
- public static class Aes128CtrKdfParams implements KdfParams {
- private int dklen;
- private int c;
- private String prf;
- private String salt;
-
- public Aes128CtrKdfParams() {}
-
- public int getDklen() {
- return dklen;
- }
-
- public void setDklen(int dklen) {
- this.dklen = dklen;
- }
-
- public int getC() {
- return c;
- }
-
- public void setC(int c) {
- this.c = c;
- }
-
- public String getPrf() {
- return prf;
- }
-
- public void setPrf(String prf) {
- this.prf = prf;
- }
-
- public String getSalt() {
- return salt;
- }
-
- public void setSalt(String salt) {
- this.salt = salt;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof Aes128CtrKdfParams)) {
- return false;
- }
-
- Aes128CtrKdfParams that = (Aes128CtrKdfParams) o;
-
- if (dklen != that.dklen) {
- return false;
- }
- if (c != that.c) {
- return false;
- }
- if (getPrf() != null ? !getPrf().equals(that.getPrf()) : that.getPrf() != null) {
- return false;
- }
- return getSalt() != null ? getSalt().equals(that.getSalt()) : that.getSalt() == null;
- }
-
- @Override
- public int hashCode() {
- int result = dklen;
- result = 31 * result + c;
- result = 31 * result + (getPrf() != null ? getPrf().hashCode() : 0);
- result = 31 * result + (getSalt() != null ? getSalt().hashCode() : 0);
- return result;
- }
- }
-
- public static class ScryptKdfParams implements KdfParams {
- private int dklen;
- private int n;
- private int p;
- private int r;
- private String salt;
-
- public ScryptKdfParams() {}
-
- public int getDklen() {
- return dklen;
- }
-
- public void setDklen(int dklen) {
- this.dklen = dklen;
- }
-
- public int getN() {
- return n;
- }
-
- public void setN(int n) {
- this.n = n;
- }
-
- public int getP() {
- return p;
- }
-
- public void setP(int p) {
- this.p = p;
- }
-
- public int getR() {
- return r;
- }
-
- public void setR(int r) {
- this.r = r;
- }
-
- public String getSalt() {
- return salt;
- }
-
- public void setSalt(String salt) {
- this.salt = salt;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof ScryptKdfParams)) {
- return false;
- }
-
- ScryptKdfParams that = (ScryptKdfParams) o;
-
- if (dklen != that.dklen) {
- return false;
- }
- if (n != that.n) {
- return false;
- }
- if (p != that.p) {
- return false;
- }
- if (r != that.r) {
- return false;
- }
- return getSalt() != null ? getSalt().equals(that.getSalt()) : that.getSalt() == null;
- }
-
- @Override
- public int hashCode() {
- int result = dklen;
- result = 31 * result + n;
- result = 31 * result + p;
- result = 31 * result + r;
- result = 31 * result + (getSalt() != null ? getSalt().hashCode() : 0);
- return result;
- }
- }
-
- // If we need to work with MyEtherWallet we'll need to use this deserializer, see the
- // following issue https://github.com/kvhnuke/etherwallet/issues/269
- static class KdfParamsDeserialiser extends JsonDeserializer {
-
- @Override
- public KdfParams deserialize(
- JsonParser jsonParser, DeserializationContext deserializationContext)
- throws IOException {
-
- ObjectMapper objectMapper = (ObjectMapper) jsonParser.getCodec();
- ObjectNode root = objectMapper.readTree(jsonParser);
- KdfParams kdfParams;
-
- // it would be preferable to detect the class to use based on the kdf parameter in the
- // container object instance
- JsonNode n = root.get("n");
- if (n == null) {
- kdfParams = objectMapper.convertValue(root, Aes128CtrKdfParams.class);
- } else {
- kdfParams = objectMapper.convertValue(root, ScryptKdfParams.class);
- }
-
- return kdfParams;
- }
- }
-
-}
diff --git a/src/main/java/io/xdag/wallet/WalletManager.java b/src/main/java/io/xdag/wallet/WalletManager.java
deleted file mode 100644
index b70efce1..00000000
--- a/src/main/java/io/xdag/wallet/WalletManager.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.xdag.crypto.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Optional;
-
-/**
- * Wallet Manager
- */
-public class WalletManager {
-
- /**
- * Create BIP44 Xdag Wallet
- *
- * @param password User Password
- * @param destinationDirectory Keystore Directory
- */
- public static Optional createBip44Wallet(String password, File destinationDirectory) throws CipherException, IOException {
- Bip39Wallet wallet = Bip44WalletUtils.generateBip44Wallet(password, destinationDirectory);
- Credentials credentials = Bip44WalletUtils.loadBip44Credentials(password, wallet.getMnemonic());
- return Optional.of(credentials);
- }
-
- /**
- * Import BIP44 Wallet From Mnemonic
- *
- * @param password User Password
- * @param mnemonic Mnemonic of 12 Worlds
- */
- public static Optional importBip44WalletFromMnemonic(String password, String mnemonic) {
- return Optional.of(Bip44WalletUtils.loadBip44Credentials(password, mnemonic));
- }
-
- /**
- * Import BIP44 Wallet From Keystore
- *
- * @param password User Password
- * @param keystore Keystore String
- */
- public static Optional importBip44WalletFromKeystore(String password, String keystore) throws JsonProcessingException, CipherException {
- ObjectMapper objectMapper = new ObjectMapper();
- WalletFile walletFile = objectMapper.readValue(keystore, WalletFile.class);
- ECKeyPair ecKeyPair = Wallet.decrypt(password, walletFile);
- return Optional.of(Credentials.create(ecKeyPair));
- }
-}
diff --git a/src/main/java/io/xdag/wallet/WalletUtils.java b/src/main/java/io/xdag/wallet/WalletUtils.java
index a52b2278..53de60fd 100644
--- a/src/main/java/io/xdag/wallet/WalletUtils.java
+++ b/src/main/java/io/xdag/wallet/WalletUtils.java
@@ -23,213 +23,30 @@
*/
package io.xdag.wallet;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.xdag.crypto.*;
-import io.xdag.utils.Numeric;
+import io.xdag.crypto.Bip32ECKeyPair;
+import io.xdag.crypto.MnemonicUtils;
+import lombok.extern.slf4j.Slf4j;
-import java.io.File;
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
+import static io.xdag.crypto.Bip32ECKeyPair.HARDENED_BIT;
-import static io.xdag.crypto.Hash.sha256;
-import static io.xdag.crypto.Keys.ADDRESS_LENGTH_IN_HEX;
-import static io.xdag.crypto.Keys.PRIVATE_KEY_LENGTH_IN_HEX;
-
-/** Utility functions for working with Wallet files. */
+@Slf4j
public class WalletUtils {
- private static final ObjectMapper objectMapper = new ObjectMapper();
- private static final SecureRandom secureRandom = SecureRandomUtils.secureRandom();
-
- static {
- objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- }
-
- public static String generateFullNewWalletFile(String password, File destinationDirectory)
- throws CipherException, IOException {
-
- return generateNewWalletFile(password, destinationDirectory, true);
- }
-
- public static String generateLightNewWalletFile(String password, File destinationDirectory)
- throws CipherException, IOException {
-
- return generateNewWalletFile(password, destinationDirectory, false);
- }
-
- public static String generateNewWalletFile(String password, File destinationDirectory)
- throws CipherException, IOException {
- return generateFullNewWalletFile(password, destinationDirectory);
- }
-
- public static String generateNewWalletFile(
- String password, File destinationDirectory, boolean useFullScrypt)
- throws CipherException, IOException {
-
- ECKeyPair ecKeyPair = Keys.createEcKeyPair();
- return generateWalletFile(password, ecKeyPair, destinationDirectory, useFullScrypt);
- }
-
- public static String generateWalletFile(
- String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)
- throws CipherException, IOException {
-
- WalletFile walletFile;
- if (useFullScrypt) {
- walletFile = Wallet.createStandard(password, ecKeyPair);
- } else {
- walletFile = Wallet.createLight(password, ecKeyPair);
- }
-
- String fileName = getWalletFileName(walletFile);
- File destination = new File(destinationDirectory, fileName);
-
- objectMapper.writeValue(destination, walletFile);
-
- return fileName;
- }
-
- /**
- * Generates a BIP-39 compatible Xdag wallet. The private key for the wallet can be
- * calculated using following algorithm:
- *
- *
- * Key = SHA-256(BIP_39_SEED(mnemonic, password))
- *
- *
- * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed
- * @param destinationDirectory The directory containing the wallet
- * @return A BIP-39 compatible Xdag wallet
- * @throws CipherException if the underlying cipher is not available
- * @throws IOException if the destination cannot be written to
- */
- public static Bip39Wallet generateBip39Wallet(String password, File destinationDirectory)
- throws CipherException, IOException {
- byte[] initialEntropy = new byte[16];
- secureRandom.nextBytes(initialEntropy);
+ // https://github.com/satoshilabs/slips/blob/master/slip-0044.md
+ public static final int XDAG_BIP44_CION_TYPE = 586;
- String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
- ECKeyPair privateKey = ECKeyPair.create(sha256(seed));
-
- String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false);
-
- return new Bip39Wallet(walletFile, mnemonic);
+ public static Bip32ECKeyPair generateBip44KeyPair(Bip32ECKeyPair master, int index) {
+ // m/44'/586'/0'/0/0
+ // xdag coin type 586 at https://github.com/satoshilabs/slips/blob/master/slip-0044.md
+ final int[] path = {44 | HARDENED_BIT, XDAG_BIP44_CION_TYPE | HARDENED_BIT, 0 | HARDENED_BIT, 0, index};
+ return Bip32ECKeyPair.deriveKeyPair(master, path);
}
- /**
- * Generates a BIP-39 compatible Xdag wallet using a mnemonic passed as argument.
- *
- * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed
- * @param mnemonic The mnemonic that will be used to generate the seed
- * @param destinationDirectory The directory containing the wallet
- * @return A BIP-39 compatible Xdag wallet
- * @throws CipherException if the underlying cipher is not available
- * @throws IOException if the destination cannot be written to
- */
- public static Bip39Wallet generateBip39WalletFromMnemonic(
- String password, String mnemonic, File destinationDirectory)
- throws CipherException, IOException {
+ public static Bip32ECKeyPair importMnemonic(Wallet wallet, String password, String mnemonic, int index) {
+ wallet.unlock(password);
byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
- ECKeyPair privateKey = ECKeyPair.create(sha256(seed));
-
- String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false);
-
- return new Bip39Wallet(walletFile, mnemonic);
- }
-
- public static Credentials loadCredentials(String password, String source)
- throws IOException, CipherException {
- return loadCredentials(password, new File(source));
+ Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
+ return generateBip44KeyPair(masterKeypair, index);
}
- public static Credentials loadCredentials(String password, File source)
- throws IOException, CipherException {
- WalletFile walletFile = objectMapper.readValue(source, WalletFile.class);
- return Credentials.create(Wallet.decrypt(password, walletFile));
- }
-
- public static Credentials loadBip39Credentials(String password, String mnemonic) {
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
- return Credentials.create(ECKeyPair.create(sha256(seed)));
- }
-
- /**
- * Load credentials from JSON wallet string.
- *
- * @param password - password to decrypt JSON wallet string
- * @param content - JSON wallet content string
- * @return Xdag credentials
- * @throws CipherException if the underlying cipher is not available
- * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error)
- * occurs
- */
- public static Credentials loadJsonCredentials(String password, String content)
- throws IOException, CipherException {
- WalletFile walletFile = objectMapper.readValue(content, WalletFile.class);
- return Credentials.create(Wallet.decrypt(password, walletFile));
- }
-
- private static String getWalletFileName(WalletFile walletFile) {
- DateTimeFormatter format =
- DateTimeFormatter.ofPattern("'UTC--'yyyy-MM-dd'T'HH-mm-ss.nVV'--'");
- ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
-
- return now.format(format) + walletFile.getAddress() + ".json";
- }
-
- public static String getDefaultKeyDirectory() {
- return getDefaultKeyDirectory(System.getProperty("os.name"));
- }
-
- static String getDefaultKeyDirectory(String osName1) {
- String osName = osName1.toLowerCase();
-
- if (osName.startsWith("mac")) {
- return String.format(
- "%s%sLibrary%sXdag",
- System.getProperty("user.home"), File.separator, File.separator);
- } else if (osName.startsWith("win")) {
- return String.format("%s%sXdag", System.getenv("APPDATA"), File.separator);
- } else {
- return String.format("%s%s.xdag", System.getProperty("user.home"), File.separator);
- }
- }
-
- public static String getTestnetKeyDirectory() {
- return String.format(
- "%s%stestnet%skeystore", getDefaultKeyDirectory(), File.separator, File.separator);
- }
-
- public static String getMainnetKeyDirectory() {
- return String.format("%s%skeystore", getDefaultKeyDirectory(), File.separator);
- }
-
- public static boolean isValidPrivateKey(String privateKey) {
- String cleanPrivateKey = Numeric.cleanHexPrefix(privateKey);
- return cleanPrivateKey.length() == PRIVATE_KEY_LENGTH_IN_HEX;
- }
-
- public static boolean isValidAddress(String input) {
- return isValidAddress(input, ADDRESS_LENGTH_IN_HEX);
- }
-
- public static boolean isValidAddress(String input, int addressLength) {
- String cleanInput = Numeric.cleanHexPrefix(input);
-
- try {
- Numeric.toBigIntNoPrefix(cleanInput);
- } catch (NumberFormatException e) {
- return false;
- }
-
- return cleanInput.length() == addressLength;
- }
}
-
diff --git a/src/test/java/io/xdag/cli/XdagCliTest.java b/src/test/java/io/xdag/cli/XdagCliTest.java
new file mode 100644
index 00000000..0cb6ef24
--- /dev/null
+++ b/src/test/java/io/xdag/cli/XdagCliTest.java
@@ -0,0 +1,464 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package io.xdag.cli;
+
+import io.xdag.config.Config;
+import io.xdag.config.DevnetConfig;
+import io.xdag.config.MainnetConfig;
+import io.xdag.config.TestnetConfig;
+import io.xdag.crypto.ECKeyPair;
+import io.xdag.crypto.Keys;
+import io.xdag.utils.BytesUtils;
+import io.xdag.wallet.Wallet;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.ExpectedSystemExit;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.*;
+
+public class XdagCliTest {
+ private Config config;
+
+ @Rule
+ public final ExpectedSystemExit exit = ExpectedSystemExit.none();
+
+ @Before
+ public void setUp() throws Exception {
+ config = new DevnetConfig();
+ }
+
+ @Test
+ public void testMain() throws Exception {
+ String[] args = { "arg1", "arg2" };
+ XdagCli cli = mock(XdagCli.class);
+ XdagCli.main(args, cli);
+ verify(cli).start(args);
+ }
+
+ @Test
+ public void testHelp() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.start(new String[] { "--help" });
+ verify(xdagCLI).printHelp();
+ }
+
+ @Test
+ public void testVersion() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.start(new String[] { "--version" });
+ verify(xdagCLI).printVersion();
+ }
+
+ @Test
+ public void testMainnet() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+
+ // mock accounts
+ List accounts = new ArrayList<>();
+ ECKeyPair account = Keys.createEcKeyPair();
+ accounts.add(account);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(wallet.getAccounts()).thenReturn(accounts);
+ when(wallet.exists()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ xdagCLI.start(new String[] {""});
+ assertTrue(xdagCLI.getConfig() instanceof MainnetConfig);
+ }
+
+ @Test
+ public void testTestnet() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+
+ // mock accounts
+ List accounts = new ArrayList<>();
+ ECKeyPair account = Keys.createEcKeyPair();
+ accounts.add(account);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(wallet.getAccounts()).thenReturn(accounts);
+ when(wallet.exists()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ xdagCLI.start(new String[] {"-t"});
+ assertTrue(xdagCLI.getConfig() instanceof TestnetConfig);
+ }
+
+ @Test
+ public void testLoadAndUnlockWalletWithWrongPassword() {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock(any())).thenReturn(false);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+
+ // mock password
+ when(xdagCLI.getPassword()).thenReturn("password");
+
+ }
+
+ @Test
+ public void testStartKernelWithEmptyWallet() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.exists()).thenReturn(false);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ doReturn(new ArrayList(), // returns empty wallet
+ Collections.singletonList(Keys.createEcKeyPair()) // returns wallet with a newly created account
+ ).when(wallet).getAccounts();
+ when(wallet.addAccount(any(ECKeyPair.class))).thenReturn(true);
+ when(wallet.flush()).thenReturn(true);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock CLI
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // mock new account
+ ECKeyPair newAccount = Keys.createEcKeyPair();
+ when(wallet.addAccountRandom()).thenReturn(newAccount);
+ when(wallet.addAccountWithNextHdKey()).thenReturn(newAccount);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn("oldpassword").when(xdagCLI).readNewPassword(any(), any());
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.start();
+
+ // verifies that a new account is added the empty wallet
+ verify(wallet).unlock("oldpassword");
+ verify(wallet, times(1)).getAccounts();
+ verify(wallet).addAccountWithNextHdKey();
+
+ verify(wallet, atLeastOnce()).flush();
+
+ // verifies that kernel starts
+ verify(xdagCLI).startKernel(any(), any());
+ }
+
+ @Test
+ public void testStartKernelWithEmptyWalletInvalidNewPassword() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.exists()).thenReturn(false);
+
+ // mock CLI
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // mock password
+ doReturn("a").doReturn("b").when(xdagCLI).readPassword(any());
+
+ // mock exits
+ doNothing().when(xdagCLI).exit(anyInt());
+
+ exit.expectSystemExitWithStatus(-1);
+ // execution
+ xdagCLI.start();
+ }
+
+ @Test
+ public void testAccountInit() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ Mockito.doNothing().when(xdagCLI).initHDAccount();
+ xdagCLI.start(new String[] { "--account", "init" });
+ verify(xdagCLI).initHDAccount();
+ }
+
+ @Test
+ public void testAccountCreate() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ Mockito.doNothing().when(xdagCLI).createAccount();
+ xdagCLI.start(new String[] { "--account", "create" });
+ verify(xdagCLI).createAccount();
+ }
+
+ @Test
+ public void testAccountList() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ Mockito.doNothing().when(xdagCLI).listAccounts();
+ xdagCLI.start(new String[] { "--account", "list" });
+ verify(xdagCLI).listAccounts();
+ }
+
+ @Test
+ public void testCreateAccount() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+ when(wallet.addAccount(any(ECKeyPair.class))).thenReturn(true);
+ when(wallet.flush()).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+
+ // mock account
+ ECKeyPair newAccount = Keys.createEcKeyPair();
+ when(wallet.addAccountRandom()).thenReturn(newAccount);
+ when(wallet.addAccountWithNextHdKey()).thenReturn(newAccount);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.createAccount();
+
+ // verification
+ verify(wallet).addAccountWithNextHdKey();
+
+ verify(wallet).flush();
+ }
+
+ @Test
+ public void testListAccounts() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock accounts
+ List accounts = new ArrayList<>();
+ ECKeyPair account = Keys.createEcKeyPair();
+ accounts.add(account);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(wallet.getAccounts()).thenReturn(accounts);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.listAccounts();
+
+ // verification
+ verify(wallet).getAccounts();
+ }
+
+ @Test
+ public void testChangePasswordIncorrectConfirmation() {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(wallet.flush()).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn("a").doReturn("b").when(xdagCLI).readPassword(any());
+ doNothing().when(xdagCLI).exit(anyInt());
+
+ // execution
+ xdagCLI.changePassword();
+ }
+
+ @Test
+ public void testDumpPrivateKey() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock account
+ ECKeyPair account = spy(Keys.createEcKeyPair());
+ String address = BytesUtils.toHexString(Keys.toBytesAddress(account));
+ byte[] addressBytes = Keys.toBytesAddress(account);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.getAccount(addressBytes)).thenReturn(account);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.dumpPrivateKey(address);
+
+ // verification
+ verify(wallet).getAccount(addressBytes);
+ verify(account).getPrivateKey();
+ }
+
+ @Test
+ public void testDumpPrivateKeyNotFound() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+ // mock address
+ String address = "c583b6ad1d1cccfc00ae9113db6408f022822b20";
+
+ byte[] addressBytes = BytesUtils.hexStringToBytes(address);
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.getAccount(addressBytes)).thenReturn(null);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.dumpPrivateKey(address);
+ }
+
+ @Test
+ public void testImportPrivateKeyExisted() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock private key
+ ECKeyPair keypair = Keys.createEcKeyPair();
+ String key = BytesUtils.toHexString(Keys.toBytesAddress(keypair));
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.addAccount(any(ECKeyPair.class))).thenReturn(false);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.importPrivateKey(key);
+ }
+
+ @Test
+ public void testImportPrivateKeyFailedToFlushWalletFile() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock private key
+ ECKeyPair keypair = Keys.createEcKeyPair();
+ String key = BytesUtils.toHexString(Keys.toBytesAddress(keypair));
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.addAccount(any(ECKeyPair.class))).thenReturn(true);
+ when(wallet.flush()).thenReturn(false);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.importPrivateKey(key);
+ }
+
+ @Test
+ public void testImportPrivateKey() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock private key
+ final String key = "302e020100300506032b657004220420bd2f24b259aac4bfce3792c31d0f62a7f28b439c3e4feb97050efe5fe254f2af";
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.addAccount(any(ECKeyPair.class))).thenReturn(true);
+ when(wallet.flush()).thenReturn(true);
+ when(wallet.isHdWalletInitialized()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ xdagCLI.importPrivateKey(key);
+ }
+
+ @Test
+ public void testImportMnemonic() throws Exception {
+ XdagCli xdagCLI = spy(new XdagCli());
+ xdagCLI.setConfig(config);
+
+ // mock private key
+ final String errorMnemonic = "aaa bbb ccc";
+ final String rightMnemonic = "view cycle bag maple hill famous black doll episode fine congress april";
+
+ // mock wallet
+ Wallet wallet = mock(Wallet.class);
+ when(wallet.unlock("oldpassword")).thenReturn(true);
+ when(xdagCLI.loadWallet()).thenReturn(wallet);
+ when(wallet.flush()).thenReturn(true);
+
+ // mock passwords
+ doReturn("oldpassword").when(xdagCLI).readPassword();
+ doReturn(null).when(xdagCLI).startKernel(any(), any());
+
+ // execution
+ assertFalse(xdagCLI.importMnemonic(errorMnemonic));
+ assertTrue(xdagCLI.importMnemonic(rightMnemonic));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/io/xdag/config/DevnetConfigTest.java b/src/test/java/io/xdag/config/DevnetConfigTest.java
index 0270573d..27941e53 100644
--- a/src/test/java/io/xdag/config/DevnetConfigTest.java
+++ b/src/test/java/io/xdag/config/DevnetConfigTest.java
@@ -1,3 +1,26 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
package io.xdag.config;
import org.apache.commons.lang3.StringUtils;
diff --git a/src/test/java/io/xdag/config/MainnetConfigTest.java b/src/test/java/io/xdag/config/MainnetConfigTest.java
index 1ce60a04..6a64c576 100644
--- a/src/test/java/io/xdag/config/MainnetConfigTest.java
+++ b/src/test/java/io/xdag/config/MainnetConfigTest.java
@@ -1,3 +1,26 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
package io.xdag.config;
import org.junit.Before;
diff --git a/src/test/java/io/xdag/config/TestnetConfigTest.java b/src/test/java/io/xdag/config/TestnetConfigTest.java
index 8577804d..bb14a6fd 100644
--- a/src/test/java/io/xdag/config/TestnetConfigTest.java
+++ b/src/test/java/io/xdag/config/TestnetConfigTest.java
@@ -1,3 +1,26 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2030 The XdagJ Developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
package io.xdag.config;
import org.junit.Before;
diff --git a/src/test/java/io/xdag/core/BlockchainTest.java b/src/test/java/io/xdag/core/BlockchainTest.java
index a21dd1a2..b7b0a6da 100644
--- a/src/test/java/io/xdag/core/BlockchainTest.java
+++ b/src/test/java/io/xdag/core/BlockchainTest.java
@@ -26,10 +26,10 @@
import com.google.common.collect.Lists;
import io.xdag.Kernel;
import io.xdag.config.Config;
-import io.xdag.config.Constants;
import io.xdag.config.DevnetConfig;
import io.xdag.crypto.ECKeyPair;
import io.xdag.crypto.Keys;
+import io.xdag.crypto.SampleKeys;
import io.xdag.crypto.jni.Native;
import io.xdag.db.DatabaseFactory;
import io.xdag.db.DatabaseName;
@@ -38,8 +38,9 @@
import io.xdag.db.store.OrphanPool;
import io.xdag.utils.BasicUtils;
import io.xdag.utils.BytesUtils;
+import io.xdag.utils.Numeric;
import io.xdag.utils.XdagTime;
-import io.xdag.wallet.OldWallet;
+import io.xdag.wallet.Wallet;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.junit.After;
@@ -48,6 +49,7 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.IOException;
import java.math.BigInteger;
import java.text.ParseException;
import java.util.*;
@@ -66,7 +68,8 @@ public class BlockchainTest {
public TemporaryFolder root = new TemporaryFolder();
Config config = new DevnetConfig();
- OldWallet xdagWallet;
+ Wallet wallet;
+ String pwd;
Kernel kernel;
DatabaseFactory dbFactory;
@@ -82,8 +85,12 @@ public void setUp() throws Exception {
if (Native.dnet_crypt_init() < 0) {
throw new Exception("dnet crypt init failed");
}
- xdagWallet = new OldWallet();
- xdagWallet.init(config);
+ pwd = "password";
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
kernel = new Kernel(config);
dbFactory = new RocksdbFactory(config);
@@ -99,11 +106,12 @@ public void setUp() throws Exception {
kernel.setBlockStore(blockStore);
kernel.setOrphanPool(orphanPool);
- kernel.setWallet(xdagWallet);
+ kernel.setWallet(wallet);
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() throws IOException {
+ wallet.delete();
}
private static void assertChainStatus(long nblocks, long nmain, long nextra, long norphan, BlockchainImpl bci) {
@@ -482,10 +490,6 @@ public void testOriginFork() throws ParseException {
}
assertEquals(secondDiff, blockchain.getXdagTopStatus().getTopDiff().toString(16));
-
-
}
-
-
}
diff --git a/src/test/java/io/xdag/core/ExtraBlockTest.java b/src/test/java/io/xdag/core/ExtraBlockTest.java
index b6c1afa6..e6e27051 100644
--- a/src/test/java/io/xdag/core/ExtraBlockTest.java
+++ b/src/test/java/io/xdag/core/ExtraBlockTest.java
@@ -28,22 +28,27 @@
import io.xdag.config.Config;
import io.xdag.config.DevnetConfig;
import io.xdag.crypto.ECKeyPair;
+import io.xdag.crypto.SampleKeys;
import io.xdag.crypto.jni.Native;
import io.xdag.db.DatabaseFactory;
import io.xdag.db.DatabaseName;
import io.xdag.db.rocksdb.RocksdbFactory;
import io.xdag.db.store.BlockStore;
import io.xdag.db.store.OrphanPool;
+import io.xdag.utils.Numeric;
import io.xdag.utils.XdagTime;
-import io.xdag.wallet.OldWallet;
+import io.xdag.wallet.Wallet;
import org.apache.commons.lang3.time.FastDateFormat;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.IOException;
import java.math.BigInteger;
import java.text.ParseException;
+import java.util.Collections;
import java.util.List;
import static io.xdag.BlockBuilder.*;
@@ -62,7 +67,8 @@ public class ExtraBlockTest {
public static FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
Config config = new DevnetConfig();
- OldWallet xdagWallet;
+ Wallet wallet;
+ String pwd;
Kernel kernel;
DatabaseFactory dbFactory;
@@ -80,8 +86,13 @@ public void setUp() throws Exception {
if (Native.dnet_crypt_init() < 0) {
throw new Exception("dnet crypt init failed");
}
- xdagWallet = new OldWallet();
- xdagWallet.init(config);
+ pwd = "password";
+ Config config = new DevnetConfig();
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
kernel = new Kernel(config);
dbFactory = new RocksdbFactory(config);
@@ -97,7 +108,7 @@ public void setUp() throws Exception {
kernel.setBlockStore(blockStore);
kernel.setOrphanPool(orphanPool);
- kernel.setWallet(xdagWallet);
+ kernel.setWallet(wallet);
}
@@ -170,4 +181,9 @@ public void testExtraBlockReUse() throws ParseException {
assertEquals(expectedExtraBlocks+1,blockchain.getXdagStats().nextra);
}
+
+ @After
+ public void tearDown() throws IOException {
+ wallet.delete();
+ }
}
diff --git a/src/test/java/io/xdag/core/RandomXSyncTest.java b/src/test/java/io/xdag/core/RandomXSyncTest.java
index 11d40a6f..99f5cdb5 100644
--- a/src/test/java/io/xdag/core/RandomXSyncTest.java
+++ b/src/test/java/io/xdag/core/RandomXSyncTest.java
@@ -29,6 +29,7 @@
import io.xdag.config.DevnetConfig;
import io.xdag.config.RandomXConstants;
import io.xdag.crypto.ECKeyPair;
+import io.xdag.crypto.SampleKeys;
import io.xdag.crypto.jni.Native;
import io.xdag.db.DatabaseFactory;
import io.xdag.db.DatabaseName;
@@ -36,8 +37,9 @@
import io.xdag.db.store.BlockStore;
import io.xdag.db.store.OrphanPool;
import io.xdag.randomx.RandomX;
+import io.xdag.utils.Numeric;
import io.xdag.utils.XdagTime;
-import io.xdag.wallet.OldWallet;
+import io.xdag.wallet.Wallet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.FastDateFormat;
import org.junit.Before;
@@ -47,6 +49,7 @@
import java.math.BigInteger;
import java.text.ParseException;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -232,9 +235,11 @@ public Kernel createKernel(TemporaryFolder root) throws Exception {
if (Native.dnet_crypt_init() < 0) {
throw new Exception("dnet crypt init failed");
}
- OldWallet xdagWallet = new OldWallet();
- xdagWallet.init(config);
- xdagWallet.createNewKey();
+ String pwd = "password";
+ Wallet wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
Kernel kernel = new Kernel(config);
@@ -251,7 +256,7 @@ public Kernel createKernel(TemporaryFolder root) throws Exception {
kernel.setBlockStore(blockStore);
kernel.setOrphanPool(orphanPool);
- kernel.setWallet(xdagWallet);
+ kernel.setWallet(wallet);
RandomX randomX = new RandomX(config);
kernel.setRandomXUtils(randomX);
diff --git a/src/test/java/io/xdag/core/RewardTest.java b/src/test/java/io/xdag/core/RewardTest.java
index a43386ca..092728c0 100644
--- a/src/test/java/io/xdag/core/RewardTest.java
+++ b/src/test/java/io/xdag/core/RewardTest.java
@@ -29,6 +29,7 @@
import io.xdag.config.DevnetConfig;
import io.xdag.config.RandomXConstants;
import io.xdag.crypto.ECKeyPair;
+import io.xdag.crypto.SampleKeys;
import io.xdag.crypto.jni.Native;
import io.xdag.db.DatabaseFactory;
import io.xdag.db.DatabaseName;
@@ -36,16 +37,19 @@
import io.xdag.db.store.BlockStore;
import io.xdag.db.store.OrphanPool;
import io.xdag.randomx.RandomX;
+import io.xdag.utils.Numeric;
import io.xdag.utils.XdagTime;
-import io.xdag.wallet.OldWallet;
-import org.apache.commons.lang3.time.FastDateFormat;
+import io.xdag.wallet.Wallet;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.IOException;
import java.math.BigInteger;
import java.text.ParseException;
+import java.util.Collections;
import java.util.List;
import static io.xdag.BlockBuilder.*;
@@ -55,14 +59,12 @@
import static org.junit.Assert.assertTrue;
public class RewardTest {
-
@Rule
public TemporaryFolder root = new TemporaryFolder();
- public static FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
-
Config config = new DevnetConfig();
- OldWallet xdagWallet;
+ Wallet wallet;
+ String pwd;
Kernel kernel;
DatabaseFactory dbFactory;
@@ -92,8 +94,12 @@ public void setUp() throws Exception {
if (Native.dnet_crypt_init() < 0) {
throw new Exception("dnet crypt init failed");
}
- xdagWallet = new OldWallet();
- xdagWallet.init(config);
+ pwd = "password";
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
kernel = new Kernel(config);
dbFactory = new RocksdbFactory(config);
@@ -109,7 +115,12 @@ public void setUp() throws Exception {
kernel.setBlockStore(blockStore);
kernel.setOrphanPool(orphanPool);
- kernel.setWallet(xdagWallet);
+ kernel.setWallet(wallet);
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ wallet.delete();
}
@Test
diff --git a/src/test/java/io/xdag/crypto/CredentialsTest.java b/src/test/java/io/xdag/crypto/CredentialsTest.java
deleted file mode 100644
index 49e3df30..00000000
--- a/src/test/java/io/xdag/crypto/CredentialsTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.crypto;
-
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class CredentialsTest {
-
- @Test
- public void testCredentialsFromString() {
- Credentials credentials = Credentials.create(SampleKeys.KEY_PAIR);
- verify(credentials);
- }
-
- @Test
- public void testCredentialsFromECKeyPair() {
- Credentials credentials =
- Credentials.create(SampleKeys.PRIVATE_KEY_STRING, SampleKeys.PUBLIC_KEY_STRING);
- verify(credentials);
- }
-
- private void verify(Credentials credentials) {
- assertEquals(credentials.getAddress(), (SampleKeys.ADDRESS));
- assertEquals(credentials.getEcKeyPair(), (SampleKeys.KEY_PAIR));
- }
-}
-
diff --git a/src/test/java/io/xdag/crypto/ECRecoverTest.java b/src/test/java/io/xdag/crypto/ECRecoverTest.java
deleted file mode 100644
index eff9c7f6..00000000
--- a/src/test/java/io/xdag/crypto/ECRecoverTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.crypto;
-
-import io.xdag.utils.Numeric;
-import org.junit.Test;
-
-import java.math.BigInteger;
-import java.util.Arrays;
-
-import static org.junit.Assert.*;
-import static io.xdag.crypto.Sign.*;
-
-public class ECRecoverTest {
- public static final String PERSONAL_MESSAGE_PREFIX = "\u0019Xdag Signed Message:\n";
-
- @Test
- public void testRecoverAddressFromSignature() {
-
- String signature =
- "0x2c6401216c9031b9a6fb8cbfccab4fcec6c951cdf40e2320108d1856eb532250576865fbcd452bcdc4c57321b619ed7a9cfd38bd973c3e1e0243ac2777fe9d5b1b";
-
- String address = "0x217a803b41360b20672ef423c76aa0a128fc3563";
- String message = "v0G9u7huK4mJb2K1";
-
- String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
- byte[] msgHash = Hash.sha256((prefix + message).getBytes());
-
- byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
- byte v = signatureBytes[64];
- if (v < 27) {
- v += 27;
- }
-
- SignatureData sd =
- new SignatureData(
- v,
- Arrays.copyOfRange(signatureBytes, 0, 32),
- Arrays.copyOfRange(signatureBytes, 32, 64));
-
- String addressRecovered = null;
- boolean match = false;
-
- // Iterate for each possible key to recover
- for (int i = 0; i < 4; i++) {
- BigInteger publicKey =
- recoverFromSignature(
- (byte) i,
- new ECDSASignature(
- new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
- msgHash);
-
- if (publicKey != null) {
- addressRecovered = "0x" + Keys.getAddress(publicKey);
-
- if (addressRecovered.equals(address)) {
- match = true;
- break;
- }
- }
- }
-
- assertEquals(addressRecovered, (address));
- assertTrue(match);
- }
-}
diff --git a/src/test/java/io/xdag/crypto/KeysTest.java b/src/test/java/io/xdag/crypto/KeysTest.java
index b903c69c..faa1a25d 100644
--- a/src/test/java/io/xdag/crypto/KeysTest.java
+++ b/src/test/java/io/xdag/crypto/KeysTest.java
@@ -70,63 +70,4 @@ public void testCreateEcKeyPair() throws Exception {
assertEquals(ecKeyPair.getPrivateKey().signum(), (1));
}
- @Test
- public void testGetAddressString() {
- assertEquals(Keys.getAddress(SampleKeys.PUBLIC_KEY_STRING), (SampleKeys.ADDRESS_NO_PREFIX));
- }
-
- @Test
- public void testGetAddressZeroPaddedAddress() {
- String publicKey =
- "0xa1b31be4d58a7ddd24b135db0da56a90fb5382077ae26b250e1dc9cd6232ce22"
- + "70f4c995428bc76aa78e522316e95d7834d725efc9ca754d043233af6ca90113";
- assertEquals(Keys.getAddress(publicKey), ("9bc8beec6d9aed67d23218941ac5fb60a1960a0c"));
- }
-
- @Test
- public void testGetAddressBigInteger() {
- assertEquals(Keys.getAddress(SampleKeys.PUBLIC_KEY), (SampleKeys.ADDRESS_NO_PREFIX));
- }
-
- @Test
- public void testGetAddressSmallPublicKey() {
- byte[] address =
- Keys.getAddress(
- Numeric.toBytesPadded(BigInteger.valueOf(0x1234), Keys.PUBLIC_KEY_SIZE));
- String expected = Numeric.toHexStringNoPrefix(address);
-
- assertEquals(Keys.getAddress("0x1234"), (expected));
- }
-
- @Test
- public void testGetAddressZeroPadded() {
- byte[] address =
- Keys.getAddress(
- Numeric.toBytesPadded(BigInteger.valueOf(0x1234), Keys.PUBLIC_KEY_SIZE));
- String expected = Numeric.toHexStringNoPrefix(address);
-
- String value = "1234";
- assertEquals(
- Keys.getAddress(
- "0x"
- + Strings.zeros(Keys.PUBLIC_KEY_LENGTH_IN_HEX - value.length())
- + value),
- (expected));
- }
-
- @Test
- public void testSerializeECKey() {
- assertArrayEquals(Keys.serialize(SampleKeys.KEY_PAIR), (ENCODED));
- }
-
- @Test
- public void testDeserializeECKey() {
- assertEquals(Keys.deserialize(ENCODED), (SampleKeys.KEY_PAIR));
- }
-
- @Test
- public void testDeserializeInvalidKey() {
- assertThrows(RuntimeException.class, () -> Keys.deserialize(new byte[0]));
- }
-
}
diff --git a/src/test/java/io/xdag/crypto/SampleKeys.java b/src/test/java/io/xdag/crypto/SampleKeys.java
index d9069b2d..5b689b15 100644
--- a/src/test/java/io/xdag/crypto/SampleKeys.java
+++ b/src/test/java/io/xdag/crypto/SampleKeys.java
@@ -36,7 +36,7 @@ public class SampleKeys {
public static final String PUBLIC_KEY_STRING =
"0x506bc1dc099358e5137292f4efdd57e400f29ba5132aa5d12b18dac1c1f6aab"
+ "a645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76";
- public static final String ADDRESS = "0xa15339b55796b3585127c180fd4cbc54612122cf";
+ public static final String ADDRESS = "0xb731bf10ed204f4ebc3d32ac88b7aa61b993fd59";
public static final String ADDRESS_NO_PREFIX = Numeric.cleanHexPrefix(ADDRESS);
public static final String PASSWORD = "Insecure Pa55w0rd";
@@ -49,8 +49,6 @@ public class SampleKeys {
public static final ECKeyPair KEY_PAIR = new ECKeyPair(PRIVATE_KEY, PUBLIC_KEY);
- public static final Credentials CREDENTIALS = Credentials.create(KEY_PAIR);
-
private SampleKeys() {}
}
diff --git a/src/test/java/io/xdag/mine/miner/MinerConnectTest.java b/src/test/java/io/xdag/mine/miner/MinerConnectTest.java
index 7c2c6f74..adc96b89 100644
--- a/src/test/java/io/xdag/mine/miner/MinerConnectTest.java
+++ b/src/test/java/io/xdag/mine/miner/MinerConnectTest.java
@@ -30,9 +30,13 @@
import io.xdag.Kernel;
import io.xdag.config.Config;
import io.xdag.config.DevnetConfig;
-import io.xdag.core.*;
+import io.xdag.core.Block;
+import io.xdag.core.BlockchainImpl;
+import io.xdag.core.ImportResult;
+import io.xdag.core.XdagBlock;
import io.xdag.crypto.ECKeyPair;
import io.xdag.crypto.Keys;
+import io.xdag.crypto.SampleKeys;
import io.xdag.crypto.jni.Native;
import io.xdag.db.DatabaseFactory;
import io.xdag.db.DatabaseName;
@@ -42,13 +46,17 @@
import io.xdag.mine.MinerChannel;
import io.xdag.mine.handler.MinerHandShakeHandler;
import io.xdag.utils.BytesUtils;
-import io.xdag.wallet.OldWallet;
+import io.xdag.utils.Numeric;
+import io.xdag.wallet.Wallet;
import org.bouncycastle.util.encoders.Hex;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.IOException;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -62,7 +70,8 @@ public class MinerConnectTest {
public TemporaryFolder root = new TemporaryFolder();
Config config = new DevnetConfig();
- OldWallet xdagWallet;
+ Wallet wallet;
+ String pwd;
Kernel kernel;
DatabaseFactory dbFactory;
MinerChannel channel;
@@ -77,8 +86,12 @@ public void setUp() throws Exception {
if (Native.dnet_crypt_init() < 0) {
throw new Exception("dnet crypt init failed");
}
- xdagWallet = new OldWallet();
- xdagWallet.init(config);
+ pwd = "password";
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
kernel = new Kernel(config);
dbFactory = new RocksdbFactory(config);
@@ -94,13 +107,18 @@ public void setUp() throws Exception {
kernel.setBlockStore(blockStore);
kernel.setOrphanPool(orphanPool);
- kernel.setWallet(xdagWallet);
+ kernel.setWallet(wallet);
blockchain = new BlockchainImpl(kernel);
channel = new MinerChannel(kernel,null,false);
}
+ @After
+ public void tearDown() throws IOException {
+ wallet.delete();
+ }
+
class MockMinerHandshakeHandler extends MinerHandShakeHandler{
public MockMinerHandshakeHandler(MinerChannel channel, Kernel kernel) {
diff --git a/src/test/java/io/xdag/wallet/Bip44WalletUtilsTest.java b/src/test/java/io/xdag/wallet/Bip44WalletUtilsTest.java
deleted file mode 100644
index 18479631..00000000
--- a/src/test/java/io/xdag/wallet/Bip44WalletUtilsTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import io.xdag.crypto.Base58;
-import io.xdag.crypto.Bip32ECKeyPair;
-import io.xdag.crypto.Credentials;
-import io.xdag.crypto.MnemonicUtils;
-import io.xdag.utils.BytesUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import static org.junit.Assert.assertEquals;
-import static io.xdag.crypto.Bip32Test.addChecksum;
-import static io.xdag.crypto.Bip32Test.serializePrivate;
-import static io.xdag.crypto.Bip32Test.serializePublic;
-import static io.xdag.crypto.SampleKeys.PASSWORD;
-
-public class Bip44WalletUtilsTest {
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Test
- public void generateBip44KeyPair() {
- String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above";
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
- String seedStr = BytesUtils.toHexString(seed);
- assertEquals(
- "f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5",
- seedStr);
-
- Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
- assertEquals(
- "xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD",
- Base58.encode(addChecksum(serializePrivate(masterKeypair))));
-
- Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair);
-
- assertEquals(
- "xprvA3bRNS6bxNHSZQvJrLiPhVePhqy69cdmsJ2oa2XuMcyuiMDn13ZAVsVDWyQRHZLJrQMMs3qUEf6GDarnJpzBKHXVFcLZgvkD9oGDR845BTL",
- Base58.encode(addChecksum(serializePrivate(bip44Keypair))));
- assertEquals(
- "xpub6GammwdVnjqjmtzmxNFQ4db8FsoaZ5MdEWxQNQwWuxWtb9YvYasR3fohNEiSmcG4pzTziN62M3LZvEowb74cgqW78BLZayCgBDRuGH89xni",
- Base58.encode(addChecksum(serializePublic(bip44Keypair))));
-
- // Verify address according to https://iancoleman.io/bip39/
- Credentials credentials = Bip44WalletUtils.loadBip44Credentials("", mnemonic);
- assertEquals("0xddc049a60750affe6f53b1d77208e4108f14d742", credentials.getAddress().toLowerCase());
- }
-
- @Test
- public void generateBip44KeyPairTestNet() {
- String mnemonic =
- "spider elbow fossil truck deal circle divert sleep safe report laundry above";
- byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
- String seedStr = BytesUtils.toHexString(seed);
- assertEquals(
- "f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5",
- seedStr);
-
- Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
- assertEquals(
- "xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD",
- Base58.encode(addChecksum(serializePrivate(masterKeypair))));
-
- Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair);
-
- assertEquals(
- "xprvA3bRNS6bxNHSZQvJrLiPhVePhqy69cdmsJ2oa2XuMcyuiMDn13ZAVsVDWyQRHZLJrQMMs3qUEf6GDarnJpzBKHXVFcLZgvkD9oGDR845BTL",
- Base58.encode(addChecksum(serializePrivate(bip44Keypair))));
- assertEquals(
- "xpub6GammwdVnjqjmtzmxNFQ4db8FsoaZ5MdEWxQNQwWuxWtb9YvYasR3fohNEiSmcG4pzTziN62M3LZvEowb74cgqW78BLZayCgBDRuGH89xni",
- Base58.encode(addChecksum(serializePublic(bip44Keypair))));
- }
-
- @Test
- public void testGenerateBip44Wallets() throws Exception {
- Bip39Wallet wallet = Bip44WalletUtils.generateBip44Wallet(PASSWORD, temporaryFolder.newFolder());
- byte[] seed = MnemonicUtils.generateSeed(wallet.getMnemonic(), PASSWORD);
- Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
- Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair);
- Credentials credentials = Credentials.create(bip44Keypair);
- assertEquals(credentials, Bip44WalletUtils.loadBip44Credentials(PASSWORD, wallet.getMnemonic()));
- }
-}
-
diff --git a/src/test/java/io/xdag/wallet/WalletFileTest.java b/src/test/java/io/xdag/wallet/WalletFileTest.java
deleted file mode 100644
index a003bc65..00000000
--- a/src/test/java/io/xdag/wallet/WalletFileTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.xdag.wallet.WalletFile;
-import org.junit.Test;
-
-import java.io.IOException;
-
-import static org.junit.Assert.assertEquals;
-
-public class WalletFileTest {
-
- @Test
- public void equalsAndHashCodeTest() throws IOException {
-
- final String AES_128_CTR =
- "{\n"
- + " \"crypto\" : {\n"
- + " \"cipher\" : \"aes-128-ctr\",\n"
- + " \"cipherparams\" : {\n"
- + " \"iv\" : \"02ebc768684e5576900376114625ee6f\"\n"
- + " },\n"
- + " \"ciphertext\" : \"7ad5c9dd2c95f34a92ebb86740b92103a5d1cc4c2eabf3b9a59e1f83f3181216\",\n"
- + " \"kdf\" : \"pbkdf2\",\n"
- + " \"kdfparams\" : {\n"
- + " \"c\" : 262144,\n"
- + " \"dklen\" : 32,\n"
- + " \"prf\" : \"hmac-sha256\",\n"
- + " \"salt\" : \"0e4cf3893b25bb81efaae565728b5b7cde6a84e224cbf9aed3d69a31c981b702\"\n"
- + " },\n"
- + " \"mac\" : \"2b29e4641ec17f4dc8b86fc8592090b50109b372529c30b001d4d96249edaf62\"\n"
- + " },\n"
- + " \"id\" : \"af0451b4-6020-4ef0-91ec-794a5a965b01\",\n"
- + " \"version\" : 3\n"
- + "}";
- ObjectMapper objectMapper = new ObjectMapper();
- WalletFile walletFile1 = objectMapper.readValue(AES_128_CTR, WalletFile.class);
-
- WalletFile.Crypto crypto = new WalletFile.Crypto();
- crypto.setCipher("aes-128-ctr");
- crypto.setCiphertext("7ad5c9dd2c95f34a92ebb86740b92103a5d1cc4c2eabf3b9a59e1f83f3181216");
- crypto.setKdf("pbkdf2");
- crypto.setMac("2b29e4641ec17f4dc8b86fc8592090b50109b372529c30b001d4d96249edaf62");
-
- WalletFile.CipherParams cipherParams = new WalletFile.CipherParams();
- cipherParams.setIv("02ebc768684e5576900376114625ee6f");
- crypto.setCipherparams(cipherParams);
-
- WalletFile.Aes128CtrKdfParams kdfParams = new WalletFile.Aes128CtrKdfParams();
- kdfParams.setC(262144);
- kdfParams.setDklen(32);
- kdfParams.setPrf("hmac-sha256");
- kdfParams.setSalt("0e4cf3893b25bb81efaae565728b5b7cde6a84e224cbf9aed3d69a31c981b702");
- crypto.setKdfparams(kdfParams);
-
- WalletFile walletFile2 = new WalletFile();
- walletFile2.setCrypto(crypto);
- walletFile2.setVersion(3);
- walletFile2.setId("af0451b4-6020-4ef0-91ec-794a5a965b01");
-
- assertEquals(walletFile1, walletFile2);
- assertEquals(walletFile1.hashCode(), walletFile2.hashCode());
- }
-}
diff --git a/src/test/java/io/xdag/wallet/WalletManagerTest.java b/src/test/java/io/xdag/wallet/WalletManagerTest.java
deleted file mode 100644
index 65f3d8f0..00000000
--- a/src/test/java/io/xdag/wallet/WalletManagerTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020-2030 The XdagJ Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package io.xdag.wallet;
-
-import io.xdag.crypto.*;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Optional;
-
-import static io.xdag.crypto.SampleKeys.PASSWORD;
-import static org.junit.Assert.*;
-
-
-public class WalletManagerTest {
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Test
- public void testCreateBip44Wallet() throws IOException, CipherException {
- Optional c = WalletManager.createBip44Wallet("xdagTotheMoon", temporaryFolder.newFolder());
- assertNotNull(c.get());
- }
-
- @Test
- public void testImportBip44WalletFromMnemonic() {
- String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above";
- Optional c = WalletManager.importBip44WalletFromMnemonic("", mnemonic);
- Credentials credentials = c.get();
- assertEquals("0xddc049a60750affe6f53b1d77208e4108f14d742", credentials.getAddress().toLowerCase());
- }
-
- @Test
- public void testImportBip44WalletFromKeystore() throws IOException, CipherException {
- InputStream in = WalletUtilsTest.class.getResourceAsStream(
- "/keyfiles/UTC--2021-04-08T13-00-52.556433000Z--a15339b55796b3585127c180fd4cbc54612122cf.json");
- String keystore = WalletUtilsTest.convertStreamToString(in);
- Optional c = WalletManager.importBip44WalletFromKeystore(PASSWORD, keystore);
- assertEquals(c.get(), SampleKeys.CREDENTIALS);
- }
-}
diff --git a/src/test/java/io/xdag/wallet/WalletTest.java b/src/test/java/io/xdag/wallet/WalletTest.java
index c6e283cf..5a1582ca 100644
--- a/src/test/java/io/xdag/wallet/WalletTest.java
+++ b/src/test/java/io/xdag/wallet/WalletTest.java
@@ -23,91 +23,176 @@
*/
package io.xdag.wallet;
-import java.io.IOException;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
+import io.xdag.config.Config;
+import io.xdag.config.DevnetConfig;
import io.xdag.crypto.ECKeyPair;
+import io.xdag.crypto.Keys;
import io.xdag.crypto.SampleKeys;
import io.xdag.utils.Numeric;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
public class WalletTest {
+ private String pwd;
+ private Wallet wallet;
+ private Config config;
+
+ @Before
+ public void setUp() {
+ pwd = "password";
+ config = new DevnetConfig();
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
+ wallet.lock();
+ }
+
+ @Test
+ public void testGetters() {
+ wallet.unlock(pwd);
+ assertEquals(pwd, wallet.getPassword());
+ }
+
@Test
- public void testCreateStandard() throws Exception {
- testCreate(Wallet.createStandard(SampleKeys.PASSWORD, SampleKeys.KEY_PAIR));
+ public void testUnlock() {
+ assertFalse(wallet.isUnlocked());
+
+ wallet.unlock(pwd);
+ assertTrue(wallet.isUnlocked());
+
+ assertEquals(1, wallet.getAccounts().size());
}
@Test
- public void testCreateLight() throws Exception {
- testCreate(Wallet.createLight(SampleKeys.PASSWORD, SampleKeys.KEY_PAIR));
+ public void testLock() {
+ wallet.unlock(pwd);
+ wallet.lock();
+ assertFalse(wallet.isUnlocked());
}
- private void testCreate(WalletFile walletFile) throws Exception {
- assertEquals(walletFile.getAddress(), (SampleKeys.ADDRESS_NO_PREFIX));
+ @Test
+ public void testAddAccounts() {
+ wallet.unlock(pwd);
+ wallet.setAccounts(Collections.emptyList());
+ ECKeyPair key1 = Keys.createEcKeyPair();
+ ECKeyPair key2 = Keys.createEcKeyPair();
+ wallet.addAccounts(Arrays.asList(key1, key2));
+ List accounts = wallet.getAccounts();
+ ECKeyPair k1 = accounts.get(0);
+ ECKeyPair k2 = accounts.get(1);
+ assertEquals(k1, key1);
+ assertEquals(k2, key2);
}
@Test
- public void testEncryptDecryptStandard() throws Exception {
- testEncryptDecrypt(Wallet.createStandard(SampleKeys.PASSWORD, SampleKeys.KEY_PAIR));
+ public void testFlush() throws InterruptedException {
+ File file = wallet.getFile();
+ long sz = wallet.getFile().length();
+ Thread.sleep(500);
+
+ wallet.unlock(pwd);
+ wallet.setAccounts(Collections.emptyList());
+ assertEquals(sz, file.length());
+
+ wallet.flush();
+ assertTrue(file.length() < sz);
}
@Test
- public void testEncryptDecryptLight() throws Exception {
- testEncryptDecrypt(Wallet.createLight(SampleKeys.PASSWORD, SampleKeys.KEY_PAIR));
+ public void testChangePassword() {
+ String pwd2 = "passw0rd2";
+
+ wallet.unlock(pwd);
+ wallet.changePassword(pwd2);
+ wallet.flush();
+ wallet.lock();
+
+ assertFalse(wallet.unlock(pwd));
+ assertTrue(wallet.unlock(pwd2));
}
- private void testEncryptDecrypt(WalletFile walletFile) throws Exception {
- assertEquals(Wallet.decrypt(SampleKeys.PASSWORD, walletFile), (SampleKeys.KEY_PAIR));
+ @Test
+ public void testAddAccountRandom() {
+ wallet.unlock(pwd);
+ int oldAccountSize = wallet.getAccounts().size();
+ wallet.addAccountRandom();
+ assertEquals(oldAccountSize + 1, wallet.getAccounts().size());
}
@Test
- public void testDecryptScrypt() throws Exception {
- WalletFile walletFile = load(SCRYPT);
- ECKeyPair ecKeyPair = Wallet.decrypt(PASSWORD, walletFile);
- assertEquals(Numeric.toHexStringNoPrefix(ecKeyPair.getPrivateKey()), (SECRET));
+ public void testRemoveAccount() {
+ wallet.unlock(pwd);
+ int oldAccountSize = wallet.getAccounts().size();
+ ECKeyPair key = Keys.createEcKeyPair();
+ wallet.addAccount(key);
+ assertEquals(oldAccountSize + 1, wallet.getAccounts().size());
+ wallet.removeAccount(key);
+ assertEquals(oldAccountSize, wallet.getAccounts().size());
+ wallet.addAccount(key);
+ assertEquals(oldAccountSize + 1, wallet.getAccounts().size());
+ wallet.removeAccount(Keys.toBytesAddress(key));
+ assertEquals(oldAccountSize, wallet.getAccounts().size());
}
@Test
- public void testGenerateRandomBytes() {
- assertArrayEquals(Wallet.generateRandomBytes(0), (new byte[] {}));
- assertEquals(Wallet.generateRandomBytes(10).length, (10));
+ public void testInitializeHdWallet() {
+ wallet.initializeHdWallet(SampleKeys.MNEMONIC);
+ assertEquals(0, wallet.getNextAccountIndex());
+ assertEquals(SampleKeys.MNEMONIC,wallet.getMnemonicPhrase());
}
- private WalletFile load(String source) throws IOException {
- ObjectMapper objectMapper = new ObjectMapper();
- return objectMapper.readValue(source, WalletFile.class);
+ @Test
+ public void testAddAccountWithNextHdKey() {
+ wallet.unlock(pwd);
+ wallet.initializeHdWallet(SampleKeys.MNEMONIC);
+ int hdkeyCount = 5;
+ for(int i = 0; i < hdkeyCount; i++) {
+ wallet.addAccountWithNextHdKey();
+ }
+ assertEquals(hdkeyCount, wallet.getNextAccountIndex());
}
- private static final String PASSWORD = "Insecure Pa55w0rd";
- private static final String SECRET =
- "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6";
-
- private static final String SCRYPT =
- "{\n"
- + " \"crypto\" : {\n"
- + " \"cipher\" : \"aes-128-ctr\",\n"
- + " \"cipherparams\" : {\n"
- + " \"iv\" : \"525d33d781892fc3c743520a248c9cd5\"\n"
- + " },\n"
- + " \"ciphertext\" : \"3c56840c492c27efeb6ded513b5816996ae10589bbd9c9662b3f1e6158729b98\",\n"
- + " \"kdf\" : \"scrypt\",\n"
- + " \"kdfparams\" : {\n"
- + " \"dklen\" : 32,\n"
- + " \"n\" : 262144,\n"
- + " \"r\" : 8,\n"
- + " \"p\" : 1,\n"
- + " \"salt\" : \"08cbb63ad8140ea2a6dedd69a723d668e83f950008049601f726660d6d50c2c8\"\n"
- + " },\n"
- + " \"mac\" : \"4691e03401d5b095a16c20cf185628cfa2d0b159021d7bc3fccac6478ce21c32\"\n"
- + " },\n"
- + " \"id\" : \"18153e96-657a-498e-a954-cba7e85d47b9\",\n"
- + " \"version\" : 3\n"
- + "}";
+ @Test
+ public void testHDKeyRecover() {
+ wallet.unlock(pwd);
+ wallet.initializeHdWallet(SampleKeys.MNEMONIC);
+ List keyPairList1 = new ArrayList<>();
+ int hdkeyCount = 5;
+ for(int i = 0; i < hdkeyCount; i++) {
+ ECKeyPair key = wallet.addAccountWithNextHdKey();
+ keyPairList1.add(key);
+ }
+
+ assertEquals(hdkeyCount, wallet.getNextAccountIndex());
+ Wallet wallet2 = new Wallet(config);
+ // use different password and same mnemonic
+ wallet2.unlock(pwd + pwd);
+ wallet2.initializeHdWallet(SampleKeys.MNEMONIC);
+ List keyPairList2 = new ArrayList<>();
+ for(int i = 0; i < hdkeyCount; i++) {
+ ECKeyPair key = wallet2.addAccountWithNextHdKey();
+ keyPairList2.add(key);
+ }
+ assertTrue(CollectionUtils.isEqualCollection(keyPairList1, keyPairList2));
+ }
+ @After
+ public void tearDown() throws IOException {
+ wallet.delete();
+ }
}
-
diff --git a/src/test/java/io/xdag/wallet/WalletUtilsTest.java b/src/test/java/io/xdag/wallet/WalletUtilsTest.java
index 0463edc5..d9e4822c 100644
--- a/src/test/java/io/xdag/wallet/WalletUtilsTest.java
+++ b/src/test/java/io/xdag/wallet/WalletUtilsTest.java
@@ -23,217 +23,102 @@
*/
package io.xdag.wallet;
+import io.xdag.config.Config;
+import io.xdag.config.DevnetConfig;
import io.xdag.crypto.*;
+import io.xdag.utils.BytesUtils;
import io.xdag.utils.Numeric;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
-import java.io.File;
-import java.nio.file.Files;
-
-
-
+import java.io.IOException;
+import java.util.Collections;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static io.xdag.crypto.Hash.sha256;
-import static io.xdag.crypto.SampleKeys.CREDENTIALS;
-import static io.xdag.crypto.SampleKeys.KEY_PAIR;
-import static io.xdag.crypto.SampleKeys.MNEMONIC;
-import static io.xdag.crypto.SampleKeys.PASSWORD;
-import static io.xdag.wallet.WalletUtils.isValidAddress;
-import static io.xdag.wallet.WalletUtils.isValidPrivateKey;
+import static io.xdag.crypto.Bip32Test.addChecksum;
+import static io.xdag.crypto.Bip32Test.serializePrivate;
+import static io.xdag.crypto.Bip32Test.serializePublic;
public class WalletUtilsTest {
- private File tempDir;
+ private String pwd;
+ private Wallet wallet;
- public static String convertStreamToString(java.io.InputStream is) {
- java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
- return s.hasNext() ? s.next() : "";
- }
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before
- public void setUp() throws Exception {
- tempDir = createTempDir();
- }
-
- @After
- public void tearDown() throws Exception {
- for (File file : tempDir.listFiles()) {
- file.delete();
- }
- tempDir.delete();
- }
-
- @Test
- public void testGenerateBip39Wallets() throws Exception {
- Bip39Wallet wallet = WalletUtils.generateBip39Wallet(PASSWORD, tempDir);
- byte[] seed = MnemonicUtils.generateSeed(wallet.getMnemonic(), PASSWORD);
- Credentials credentials = Credentials.create(ECKeyPair.create(sha256(seed)));
-
- assertEquals(credentials, WalletUtils.loadBip39Credentials(PASSWORD, wallet.getMnemonic()));
- }
-
- @Test
- public void testGenerateBip39WalletFromMnemonic() throws Exception {
- Bip39Wallet wallet =
- WalletUtils.generateBip39WalletFromMnemonic(PASSWORD, MNEMONIC, tempDir);
- byte[] seed = MnemonicUtils.generateSeed(wallet.getMnemonic(), PASSWORD);
- Credentials credentials = Credentials.create(ECKeyPair.create(sha256(seed)));
-
- assertEquals(credentials, WalletUtils.loadBip39Credentials(PASSWORD, wallet.getMnemonic()));
- }
-
- @Test
- public void testGenerateFullNewWalletFile() throws Exception {
- String fileName = WalletUtils.generateFullNewWalletFile(PASSWORD, tempDir);
- testGeneratedNewWalletFile(fileName);
- }
-
- @Test
- public void testGenerateNewWalletFile() throws Exception {
- String fileName = WalletUtils.generateNewWalletFile(PASSWORD, tempDir);
- testGeneratedNewWalletFile(fileName);
- }
-
- @Test
- public void testGenerateLightNewWalletFile() throws Exception {
- String fileName = WalletUtils.generateLightNewWalletFile(PASSWORD, tempDir);
- testGeneratedNewWalletFile(fileName);
- }
-
- private void testGeneratedNewWalletFile(String fileName) throws Exception {
- WalletUtils.loadCredentials(PASSWORD, new File(tempDir, fileName));
- }
-
- @Test
- public void testGenerateFullWalletFile() throws Exception {
- String fileName = WalletUtils.generateWalletFile(PASSWORD, KEY_PAIR, tempDir, true);
- testGenerateWalletFile(fileName);
+ public void setUp() {
+ pwd = "password";
+ Config config = new DevnetConfig();
+ wallet = new Wallet(config);
+ wallet.unlock(pwd);
+ ECKeyPair key = ECKeyPair.create(Numeric.toBigInt(SampleKeys.PRIVATE_KEY_STRING));
+ wallet.setAccounts(Collections.singletonList(key));
+ wallet.flush();
+ wallet.lock();
}
@Test
- public void testGenerateLightWalletFile() throws Exception {
- String fileName = WalletUtils.generateWalletFile(PASSWORD, KEY_PAIR, tempDir, false);
- testGenerateWalletFile(fileName);
- }
+ public void generateBip44KeyPair() {
- private void testGenerateWalletFile(String fileName) throws Exception {
- Credentials credentials =
- WalletUtils.loadCredentials(PASSWORD, new File(tempDir, fileName));
-
- assertEquals(credentials, (CREDENTIALS));
- }
-
- @Test
- public void testLoadCredentialsFromFile() throws Exception {
- Credentials credentials =
- WalletUtils.loadCredentials(
- PASSWORD,
- new File(
- WalletUtilsTest.class
- .getResource(
- "/keyfiles/UTC--2021-04-08T13-00-52.556433000Z--a15339b55796b3585127c180fd4cbc54612122cf.json")
- .getFile()));
-
- assertEquals(credentials, (CREDENTIALS));
- }
+ String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above";
+ byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
+ String seedStr = BytesUtils.toHexString(seed);
+ assertEquals(
+ "f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5",
+ seedStr);
- @Test
- public void testLoadCredentialsFromString() throws Exception {
- Credentials credentials =
- WalletUtils.loadCredentials(
- PASSWORD,
- WalletUtilsTest.class
- .getResource(
- "/keyfiles/UTC--2021-04-08T13-00-52.556433000Z--a15339b55796b3585127c180fd4cbc54612122cf.json")
- .getFile());
-
- assertEquals(credentials, (CREDENTIALS));
- }
+ Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
+ assertEquals(
+ "xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD",
+ Base58.encode(addChecksum(serializePrivate(masterKeypair))));
- @Test
- public void testLoadCredentialsMyEtherWallet() throws Exception {
- Credentials credentials =
- WalletUtils.loadCredentials(
- PASSWORD,
- new File(
- WalletUtilsTest.class
- .getResource(
- "/keyfiles/UTC--2021-04-08T13-00-52.556433000Z--a15339b55796b3585127c180fd4cbc54612122cf.json")
- .getFile()));
+ Bip32ECKeyPair bip44Keypair = WalletUtils.generateBip44KeyPair(masterKeypair,0);
assertEquals(
- credentials,
- (Credentials.create(
- "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6")));
- }
-
- @Test
- public void testLoadJsonCredentials() throws Exception {
- Credentials credentials =
- WalletUtils.loadJsonCredentials(
- PASSWORD,
- convertStreamToString(
- WalletUtilsTest.class.getResourceAsStream(
- "/keyfiles/UTC--2021-04-08T13-00-52.556433000Z--a15339b55796b3585127c180fd4cbc54612122cf.json")));
-
- assertEquals(credentials, (CREDENTIALS));
- }
+ "xprvA3bRNS6bxNHSZQvJrLiPhVePhqy69cdmsJ2oa2XuMcyuiMDn13ZAVsVDWyQRHZLJrQMMs3qUEf6GDarnJpzBKHXVFcLZgvkD9oGDR845BTL",
+ Base58.encode(addChecksum(serializePrivate(bip44Keypair))));
+ assertEquals(
+ "xpub6GammwdVnjqjmtzmxNFQ4db8FsoaZ5MdEWxQNQwWuxWtb9YvYasR3fohNEiSmcG4pzTziN62M3LZvEowb74cgqW78BLZayCgBDRuGH89xni",
+ Base58.encode(addChecksum(serializePublic(bip44Keypair))));
- @Test
- public void testGetDefaultKeyDirectory() {
- assertTrue(
- WalletUtils.getDefaultKeyDirectory("Mac OS X")
- .endsWith(
- String.format(
- "%sLibrary%sXdag", File.separator, File.separator)));
- assertTrue(
- WalletUtils.getDefaultKeyDirectory("Windows")
- .endsWith(String.format("%sXdag", File.separator)));
- assertTrue(
- WalletUtils.getDefaultKeyDirectory("Linux")
- .endsWith(String.format("%s.xdag", File.separator)));
+ // Verify address according to https://iancoleman.io/bip39/
+ Bip32ECKeyPair key = WalletUtils.importMnemonic(wallet, pwd, mnemonic, 0);
+ assertEquals("d85a4d67fcb69b14b12a15ad60e5dc65852f9907", BytesUtils.toHexString(Keys.toBytesAddress(key)));
}
@Test
- public void testGetTestnetKeyDirectory() {
- assertTrue(
- WalletUtils.getMainnetKeyDirectory()
- .endsWith(String.format("%skeystore", File.separator)));
- assertTrue(
- WalletUtils.getTestnetKeyDirectory()
- .endsWith(
- String.format(
- "%stestnet%skeystore", File.separator, File.separator)));
- }
+ public void generateBip44KeyPairTestNet() {
+ String mnemonic =
+ "spider elbow fossil truck deal circle divert sleep safe report laundry above";
+ byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
+ String seedStr = BytesUtils.toHexString(seed);
+ assertEquals(
+ "f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5",
+ seedStr);
- static File createTempDir() throws Exception {
- return Files.createTempDirectory(WalletUtilsTest.class.getSimpleName() + "-testkeys")
- .toFile();
- }
+ Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
+ assertEquals(
+ "xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD",
+ Base58.encode(addChecksum(serializePrivate(masterKeypair))));
- @Test
- public void testIsValidPrivateKey() {
- assertTrue(isValidPrivateKey(SampleKeys.PRIVATE_KEY_STRING));
- assertTrue(isValidPrivateKey(Numeric.prependHexPrefix(SampleKeys.PRIVATE_KEY_STRING)));
+ Bip32ECKeyPair bip44Keypair = WalletUtils.generateBip44KeyPair(masterKeypair,0);
- assertFalse(isValidPrivateKey(""));
- assertFalse(isValidPrivateKey(SampleKeys.PRIVATE_KEY_STRING + "a"));
- assertFalse(isValidPrivateKey(SampleKeys.PRIVATE_KEY_STRING.substring(1)));
+ assertEquals(
+ "xprvA3bRNS6bxNHSZQvJrLiPhVePhqy69cdmsJ2oa2XuMcyuiMDn13ZAVsVDWyQRHZLJrQMMs3qUEf6GDarnJpzBKHXVFcLZgvkD9oGDR845BTL",
+ Base58.encode(addChecksum(serializePrivate(bip44Keypair))));
+ assertEquals(
+ "xpub6GammwdVnjqjmtzmxNFQ4db8FsoaZ5MdEWxQNQwWuxWtb9YvYasR3fohNEiSmcG4pzTziN62M3LZvEowb74cgqW78BLZayCgBDRuGH89xni",
+ Base58.encode(addChecksum(serializePublic(bip44Keypair))));
}
- @Test
- public void testIsValidAddress() {
- assertTrue(isValidAddress(SampleKeys.ADDRESS));
- assertTrue(isValidAddress(SampleKeys.ADDRESS_NO_PREFIX));
-
- assertFalse(isValidAddress(""));
- assertFalse(isValidAddress(SampleKeys.ADDRESS + 'a'));
- assertFalse(isValidAddress(SampleKeys.ADDRESS.substring(1)));
+ @After
+ public void tearDown() throws IOException {
+ wallet.delete();
}
}
From 91b24c8e572cbab1919a1a568241beed40633332 Mon Sep 17 00:00:00 2001
From: punk8 <1401501690@qq.com>
Date: Mon, 31 May 2021 11:33:35 +0800
Subject: [PATCH 05/11] add unittest for rpc
---
docs/XDAGJ_Cli_Wallet_eng.md | 173 ++++++++++++++++
pom.xml | 15 +-
src/main/java/io/xdag/Kernel.java | 16 +-
src/main/java/io/xdag/cli/XdagCli.java | 2 +-
src/main/java/io/xdag/cli/XdagOption.java | 2 +
.../java/io/xdag/config/AbstractConfig.java | 13 ++
src/main/java/io/xdag/config/Config.java | 1 +
src/main/java/io/xdag/core/ImportResult.java | 2 +-
.../java/io/xdag/mine/handler/Miner03.java | 2 +-
.../io/xdag/rpc/dto/ETHBlockResultDTO.java | 62 ++++++
.../rpc/dto/ETHTransactionReceiptDTO.java | 53 +++++
.../xdag/rpc/dto/TransactionReceiptDTO.java | 92 +++++++++
.../io/xdag/rpc/modules/eth/EthModule.java | 45 ++++
.../rpc/modules/eth/EthModuleTransaction.java | 28 +++
.../xdag/rpc/modules/eth/EthModuleWallet.java | 8 +
.../xdag/rpc/modules/web3/Web3EthModule.java | 193 +++++++++---------
.../rpc/modules/web3/Web3EthModuleImpl.java | 69 +++++++
.../rpc/modules/web3/Web3XdagModuleImpl.java | 4 +-
.../xdag/XdagModuleTransactionBase.java | 24 ++-
.../xdag/XdagModuleTransactionDisabled.java | 5 +
.../xdag/XdagModuleTransactionEnabled.java | 16 ++
src/main/resources/xdag-testnet.config | 59 +-----
.../java/io/xdag/rpc/Web3XdagModuleTest.java | 90 ++++++++
.../io/xdag/rpc/modules/XdagModuleTest.java | 30 +++
.../io/xdag/rpc/netty/Web3HttpServerTest.java | 172 ++++++++++++++++
.../rpc/netty/Web3WebSocketServerTest.java | 159 +++++++++++++++
26 files changed, 1169 insertions(+), 166 deletions(-)
create mode 100644 docs/XDAGJ_Cli_Wallet_eng.md
create mode 100644 src/main/java/io/xdag/rpc/dto/ETHBlockResultDTO.java
create mode 100644 src/main/java/io/xdag/rpc/dto/ETHTransactionReceiptDTO.java
create mode 100644 src/main/java/io/xdag/rpc/dto/TransactionReceiptDTO.java
create mode 100644 src/main/java/io/xdag/rpc/modules/eth/EthModule.java
create mode 100644 src/main/java/io/xdag/rpc/modules/eth/EthModuleTransaction.java
create mode 100644 src/main/java/io/xdag/rpc/modules/eth/EthModuleWallet.java
create mode 100644 src/main/java/io/xdag/rpc/modules/web3/Web3EthModuleImpl.java
create mode 100644 src/main/java/io/xdag/rpc/modules/xdag/XdagModuleTransactionEnabled.java
create mode 100644 src/test/java/io/xdag/rpc/Web3XdagModuleTest.java
create mode 100644 src/test/java/io/xdag/rpc/modules/XdagModuleTest.java
create mode 100644 src/test/java/io/xdag/rpc/netty/Web3HttpServerTest.java
create mode 100644 src/test/java/io/xdag/rpc/netty/Web3WebSocketServerTest.java
diff --git a/docs/XDAGJ_Cli_Wallet_eng.md b/docs/XDAGJ_Cli_Wallet_eng.md
new file mode 100644
index 00000000..8564b211
--- /dev/null
+++ b/docs/XDAGJ_Cli_Wallet_eng.md
@@ -0,0 +1,173 @@
+# XDAGJ Command Line Interface Wallet Tutorial
+
+This section describes wallet related 'xdag-cli' commands.
+
+If you want to learn the structure of your Xdag wallet, you can play with the "xdag-cli" wallet commands as shown below.
+
+- **Please note that at least keep the wallet password and mnemonic phrase, once lost, you cannot retrieve it**
+- **This wallet is still in the testing stage, there may be security issues, loss or other unknown issues**
+
+----------
+
+- [System environment](#system-environment)
+- [Usage](#usage)
+----------
+## System environment
+
+```yaml
+ JDK : v15
+ script : xdag.sh
+ jar : xdagj-0.x.y-shaded.jar
+```
+
+example:
+```shell
+xdager@localhost xdag_full_node % java --version
+openjdk 15.0.2 2021-01-19
+OpenJDK Runtime Environment (build 15.0.2+7)
+OpenJDK 64-Bit Server VM (build 15.0.2+7, mixed mode, sharing)
+xdager@localhost xdag_full_node % mkdir -p /usr/local/xdag_full_node && cd /usr/local/xdag_full_node
+xdager@localhost xdag_full_node % pwd
+/usr/local/xdag_full_node
+xdager@localhost xdag_full_node % chmod +x xdag.sh
+xdager@localhost xdag_full_node % ll
+total 95464
+drwxr-xr-x 4 xdager wheel 128 5 24 14:22 .
+drwxr-xr-x 16 xdager wheel 512 5 24 14:21 ..
+-rwxr-xr-x 1 xdager wheel 275 5 24 14:21 xdag.sh
+-rw-r--r-- 1 xdager wheel 48869970 5 24 14:21 xdagj-0.4.2-shaded.jar
+```
+
+## Usage
+
+### account
+
+xdag account initialization and creation, query and other functions
+
+#### init
+
+The init action will create an initial mnemonic for HDWallet, please remember the mnemonic for future recovery
+The wallet follows the bip44 standard, [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki), [XDAG CoinType](https://github.com/satoshilabs/slips/blob/master/slip-0044.md)
+
+```yaml
+m / purpose' / coin_type' / account' / change / address_index
+```
+
+```yaml
+purporse': Fixed value 44', which means BIP44
+coin_type': This represents the currency, which can be compatible with many currencies, such as BTC is 0', ETH is 60',XDAG is 586'
+
+btc: m/44'/0'/0'/0
+eth: m/44'/60'/0'/0
+xdag: m/44'/586'/0'/0
+```
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --account init
+EnterNewPassword:
+ReEnterNewPassword:
+HdWallet Initializing...
+HdWallet Mnemonic:smoke that notice super program endless culture bubble trial pass annual rule
+HdWallet Mnemonic Repeat:smoke that notice super program endless culture bubble trial pass annual rule
+HdWallet Initialized Successfully!
+```
+
+#### create
+
+Please note that the create action must be created after the init action, which will follow the bip32 standard,
+[BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --account create
+Please enter your password:
+New Address:89fc1798f2e39687f566db6c02838990206978be
+PublicKey:0092909df115b0646f5f771a0505c9c55cc875aeea2a440b7dd11afbd54b350482749d7dba68d1bff96c19adb1a6e920be2e959a684a8ab7545a9a213d85dc4a57
+```
+#### list
+The list action will list all addresses in the wallet.data file, including HDWallet and randomly generated addresses
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --account list
+Please enter your password:
+Address:0 89fc1798f2e39687f566db6c02838990206978b
+```
+
+### changepassword
+
+This command allows the user to change the password of the wallet.data wallet file generated by xdagj
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --changepassword
+Please enter your password:
+EnterNewPassword
+ReEnterNewPassword
+Password Changed Successfully!
+```
+User needs to enter the old password and the new password, and the two new passwords are the same
+
+### convertoldwallet
+
+This command is used to convert the wallet.dat file generated by the old wallet of the xdag c version (please note that the wallet name generated by xdagj is wallet.data).
+* Before convert command, you need copy wallet.dat to xdagj working dir.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --convertoldwallet wallet.dat
+PrivateKey:4a7dc39ff17b8b48b42edf856a0cf956e29741b02da3f00fff60967d55cad907
+ PublicKey:4b9e26714f33134572cce57d06edbb502fb819d524b66610976f25843749f123ee8f4beedd22ff5126e8d27a7abb7d64a54927c9bbfb5fc0d66777bc10f1a646
+Old Wallet Converted Successfully!
+```
+
+### dumpprivatekey
+
+Dump the private key in hexadecimal string format.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --dumpprivatekey 47af7c90483f95ff030f12458071d0528ac3c5d0
+Private:4a7dc39ff17b8b48b42edf856a0cf956e29741b02da3f00fff60967d55cad907
+Private Dump Successfully!
+```
+
+### importprivatekey
+
+Import the private key in hexadecimal string format.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --importprivatekey 4a7dc39ff17b8b48b42edf856a0cf956e29741b02da3f00fff60967d55cad907
+Address:47af7c90483f95ff030f12458071d0528ac3c5d0
+PublicKey:4b9e26714f33134572cce57d06edbb502fb819d524b66610976f25843749f123ee8f4beedd22ff5126e8d27a7abb7d64a54927c9bbfb5fc0d66777bc10f1a646
+Private Key Imported Successfully!
+```
+
+### importmnemonic
+
+Use mnemonic phrase import to generate new HDWallet.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --importprivatekey 4a7dc39ff17b8b48b42edf856a0cf956e29741b02da3f00fff60967d55cad907
+Address:47af7c90483f95ff030f12458071d0528ac3c5d0
+PublicKey:4b9e26714f33134572cce57d06edbb502fb819d524b66610976f25843749f123ee8f4beedd22ff5126e8d27a7abb7d64a54927c9bbfb5fc0d66777bc10f1a646
+HDWallet Mnemonic Imported Successfully!
+```
+
+### help
+
+Prompt for command line help information.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --help
+usage: ./xdag.sh [options]
+--account init|create|list
+--changepassword change wallet password
+--convertoldwallet convert xdag old wallet.dat to private key hex
+--dumpprivatekey print hex key
+--help print help
+--importmnemonic import HDWallet mnemonic
+--importprivatekey import hex key
+--version show version
+```
+
+### version
+
+Show xdagj version number.
+
+```shell
+xdager@localhost xdag_full_node % ./xdag.sh --version
+0.4.3
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 9ad25efd..1cc77421 100644
--- a/pom.xml
+++ b/pom.xml
@@ -433,7 +433,7 @@
com.squareup.okhttp3
okhttp
- 4.8.1
+ 4.9.1
org.jetbrains
@@ -441,6 +441,13 @@
+
+
+
+
+
+
+
org.mockito
@@ -599,6 +606,12 @@
+
+ com.sun.xml.ws
+ jaxws-ri
+ 2.3.0
+ pom
+
diff --git a/src/main/java/io/xdag/Kernel.java b/src/main/java/io/xdag/Kernel.java
index 4b5ff070..aa2f2312 100644
--- a/src/main/java/io/xdag/Kernel.java
+++ b/src/main/java/io/xdag/Kernel.java
@@ -66,6 +66,7 @@
import io.xdag.rpc.modules.web3.Web3XdagModuleImpl;
import io.xdag.rpc.modules.xdag.XdagModule;
import io.xdag.rpc.modules.xdag.XdagModuleTransactionDisabled;
+import io.xdag.rpc.modules.xdag.XdagModuleTransactionEnabled;
import io.xdag.rpc.modules.xdag.XdagModuleWalletDisabled;
import io.xdag.rpc.netty.*;
import io.xdag.rpc.serialize.JacksonBasedRpcSerializer;
@@ -305,8 +306,10 @@ public synchronized void testStart() throws Exception {
// ====================================
// rpc start
// ====================================
- getWeb3HttpServer().start();
- getWeb3WebSocketServer().start();
+ if (config.isRPCEnabled()) {
+ getWeb3HttpServer().start();
+ getWeb3WebSocketServer().start();
+ }
// ====================================
// telnet server
@@ -325,7 +328,7 @@ private Web3 getWeb3() {
}
private Web3 buildWeb3() {
- Web3XdagModule web3XdagModule = new Web3XdagModuleImpl(this.getBlockchain(),new XdagModule((byte) 0x1,new XdagModuleWalletDisabled(),new XdagModuleTransactionDisabled()),this);
+ Web3XdagModule web3XdagModule = new Web3XdagModuleImpl(new XdagModule((byte) 0x1,new XdagModuleWalletDisabled(),new XdagModuleTransactionEnabled(this.getBlockchain())),this);
return new Web3Impl(web3XdagModule);
}
@@ -358,7 +361,7 @@ private Web3WebSocketServer getWeb3WebSocketServer() throws UnknownHostException
private Web3HttpServer getWeb3HttpServer() throws UnknownHostException {
if (web3HttpServer == null) {
web3HttpServer = new Web3HttpServer(
- InetAddress.getByName("127.0.0.1"),
+ InetAddress.getByName("192.168.3.229"),
4445,
123,
true,
@@ -375,7 +378,7 @@ private JsonRpcWeb3FilterHandler getJsonRpcWeb3FilterHandler() throws UnknownHos
if (jsonRpcWeb3FilterHandler == null) {
jsonRpcWeb3FilterHandler = new JsonRpcWeb3FilterHandler(
"*",
- InetAddress.getByName("127.0.0.1"),
+ InetAddress.getByName("192.168.3.229"),
null
);
}
@@ -403,6 +406,9 @@ public synchronized void testStop() {
if (web3HttpServer != null) {
web3HttpServer.stop();
}
+ if (web3WebSocketServer != null) {
+ web3WebSocketServer.stop();
+ }
// 1. 工作层关闭
// stop consensus
diff --git a/src/main/java/io/xdag/cli/XdagCli.java b/src/main/java/io/xdag/cli/XdagCli.java
index 119f1063..652516c3 100644
--- a/src/main/java/io/xdag/cli/XdagCli.java
+++ b/src/main/java/io/xdag/cli/XdagCli.java
@@ -483,4 +483,4 @@ public String readPassword(String prompt) {
public String readPassword() {
return readPassword("Please enter your password: ");
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/io/xdag/cli/XdagOption.java b/src/main/java/io/xdag/cli/XdagOption.java
index 1fb8d0d2..fd6743e1 100644
--- a/src/main/java/io/xdag/cli/XdagOption.java
+++ b/src/main/java/io/xdag/cli/XdagOption.java
@@ -41,6 +41,8 @@ public enum XdagOption {
IMPORT_MNEMONIC("importmnemonic"),
+ ENABLE_RPC("enablerpc"),
+
CONVERT_OLD_WALLET("convertoldwallet");
private final String name;
diff --git a/src/main/java/io/xdag/config/AbstractConfig.java b/src/main/java/io/xdag/config/AbstractConfig.java
index 40bc2192..710f65a1 100644
--- a/src/main/java/io/xdag/config/AbstractConfig.java
+++ b/src/main/java/io/xdag/config/AbstractConfig.java
@@ -140,6 +140,7 @@ public class AbstractConfig implements Config, AdminSpec, PoolSpec, NodeSpec, Wa
// Xdag RPC modules
// =========================
protected List moduleDescriptions;
+ protected boolean rpcEnabled = false;
public void setDir() {
@@ -249,6 +250,8 @@ public void getSetting() {
log.debug("{} IP access", list.length);
whiteIPList.addAll(Arrays.asList(list));
}
+
+ rpcEnabled = setting.getBool("isRPCEnabled") != null && setting.getBool("isRPCEnabled");
}
@Override
@@ -336,6 +339,11 @@ public boolean enableRefresh() {
@Override
public List getRpcModules() {
+
+ if (!rpcEnabled) {
+ return null;
+ }
+
if (this.moduleDescriptions != null) {
return this.moduleDescriptions;
}
@@ -378,4 +386,9 @@ public List getRpcModules() {
return modules;
}
+
+ @Override
+ public boolean isRPCEnabled() {
+ return rpcEnabled;
+ }
}
diff --git a/src/main/java/io/xdag/config/Config.java b/src/main/java/io/xdag/config/Config.java
index 41f77121..9460bff0 100644
--- a/src/main/java/io/xdag/config/Config.java
+++ b/src/main/java/io/xdag/config/Config.java
@@ -83,5 +83,6 @@ public interface Config {
// rpc
List getRpcModules();
+ boolean isRPCEnabled();
}
diff --git a/src/main/java/io/xdag/core/ImportResult.java b/src/main/java/io/xdag/core/ImportResult.java
index dbc07618..94c61f01 100644
--- a/src/main/java/io/xdag/core/ImportResult.java
+++ b/src/main/java/io/xdag/core/ImportResult.java
@@ -51,7 +51,7 @@ public String getErrorInfo() {
return errorInfo;
}
- public boolean isIllegal() {
+ public boolean isLegal() {
return this == IMPORTED_NOT_BEST || this == IMPORTED_BEST || this == EXIST;
}
}
diff --git a/src/main/java/io/xdag/mine/handler/Miner03.java b/src/main/java/io/xdag/mine/handler/Miner03.java
index ce9cbcbf..a1e45835 100644
--- a/src/main/java/io/xdag/mine/handler/Miner03.java
+++ b/src/main/java/io/xdag/mine/handler/Miner03.java
@@ -97,7 +97,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
protected void processNewBlock(NewBlockMessage msg) {
Block block = msg.getBlock();
ImportResult importResult = syncManager.validateAndAddNewBlock(new BlockWrapper(block, kernel.getConfig().getNodeSpec().getTTL()));
- if (importResult.isIllegal()) {
+ if (importResult.isLegal()) {
log.info("XDAG:receive tansaction. A Transaction from wallet/miner, block hash:{}", Hex.toHexString(block.getHash()));
}
}
diff --git a/src/main/java/io/xdag/rpc/dto/ETHBlockResultDTO.java b/src/main/java/io/xdag/rpc/dto/ETHBlockResultDTO.java
new file mode 100644
index 00000000..1edb3b04
--- /dev/null
+++ b/src/main/java/io/xdag/rpc/dto/ETHBlockResultDTO.java
@@ -0,0 +1,62 @@
+package io.xdag.rpc.dto;
+
+import lombok.Data;
+
+import java.util.Collections;
+import java.util.List;
+
+import static io.xdag.rpc.utils.TypeConverter.toQuantityJsonHex;
+
+@Data
+public class ETHBlockResultDTO {
+ private final String number; // QUANTITY - the block number. null when its pending block.
+ private final String hash; // DATA, 32 Bytes - hash of the block. null when its pending block.
+ private final String parentHash; // DATA, 32 Bytes - hash of the parent block.
+ private final String sha3Uncles; // DATA, 32 Bytes - SHA3 of the uncles data in the block.
+ private final String logsBloom; // DATA, 256 Bytes - the bloom filter for the logs of the block. null when its pending block.
+ private final String transactionsRoot; // DATA, 32 Bytes - the root of the transaction trie of the block.
+ private final String stateRoot; // DATA, 32 Bytes - the root of the final state trie of the block.
+ private final String receiptsRoot; // DATA, 32 Bytes - the root of the receipts trie of the block.
+ private final String miner; // DATA, 20 Bytes - the address of the beneficiary to whom the mining rewards were given.
+ private final String difficulty; // QUANTITY - integer of the difficulty for this block.
+ private final String totalDifficulty; // QUANTITY - integer of the total difficulty of the chain until this block.
+ private final String extraData; // DATA - the "extra data" field of this block
+ private final String size;//QUANTITY - integer the size of this block in bytes.
+ private final String gasLimit;//: QUANTITY - the maximum gas allowed in this block.
+ private final String gasUsed; // QUANTITY - the total used gas by all transactions in this block.
+ private final String timestamp; //: QUANTITY - the unix timestamp for when the block was collated.
+ private final List