diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg
index 422739d..5dc08e3 100644
--- a/.github/badges/branches.svg
+++ b/.github/badges/branches.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index 50d0d19..4b6025e 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/README.md b/README.md
index bac75e8..3d313cd 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,39 @@ cd sui/target/release
TODO
+## Supported APIs
+- [ ] sui_batchTransaction
+- [x] sui_dryRunTransaction
+- [ ] sui_executeTransaction
+- [ ] sui_getCoinMetadata
+- [x] sui_getCommitteeInfo
+- [x] sui_getEvents
+- [x] sui_getMoveFunctionArgTypes
+- [x] sui_getNormalizedMoveFunction
+- [x] sui_getNormalizedMoveModule
+- [x] sui_getNormalizedMoveModulesByPackage
+- [x] sui_getNormalizedMoveStruct
+- [x] sui_getObject
+- [x] sui_getObjectsOwnedByAddress
+- [x] sui_getObjectsOwnedByObject
+- [x] sui_getRawObject
+- [x] sui_getTotalTransactionNumber
+- [x] sui_getTransaction
+- [ ] sui_getTransactions
+- [x] sui_getTransactionsInRange
+- [x] sui_mergeCoins
+- [x] sui_moveCall
+- [x] sui_pay
+- [x] sui_payAllSui
+- [x] sui_paySui
+- [x] sui_publish
+- [x] sui_splitCoin
+- [x] sui_splitCoinEqual
+- [ ] sui_subscribeEvent
+- [x] sui_transferObject
+- [x] sui_transferSui
+- [ ] sui_tryGetPastObject
+
## Examples
### create client
diff --git a/build.gradle b/build.gradle
index 0eb932c..be92a5a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,6 +25,14 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
implementation 'org.apache.commons:commons-lang3:3.12.0'
+ // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on
+ implementation 'org.bouncycastle:bcprov-jdk18on:1.72'
+ // https://mvnrepository.com/artifact/org.web3j/crypto
+ implementation('org.web3j:crypto:4.9.5') {
+ exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
+ exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
+ exclude group: 'org.slf4j', module: 'slf4j-api'
+ }
// https://mvnrepository.com/artifact/com.squareup.okhttp3/mockwebserver
testImplementation 'com.squareup.okhttp3:mockwebserver:4.10.0'
diff --git a/src/integrationTest/java/io/sui/ExecutionClientImplIntTests.java b/src/integrationTest/java/io/sui/ExecutionClientImplIntTests.java
index c922822..e718283 100644
--- a/src/integrationTest/java/io/sui/ExecutionClientImplIntTests.java
+++ b/src/integrationTest/java/io/sui/ExecutionClientImplIntTests.java
@@ -17,6 +17,8 @@
package io.sui;
+import io.sui.clients.ExecutionClient;
+import io.sui.clients.ExecutionClientImpl;
import io.sui.jsonrpc.GsonJsonHandler;
import io.sui.jsonrpc.JsonHandler;
import io.sui.jsonrpc.JsonRpcClientProvider;
diff --git a/src/integrationTest/java/io/sui/JsonRpcTransactionBuilderIntTests.java b/src/integrationTest/java/io/sui/JsonRpcTransactionBuilderIntTests.java
index 4494361..56d83f6 100644
--- a/src/integrationTest/java/io/sui/JsonRpcTransactionBuilderIntTests.java
+++ b/src/integrationTest/java/io/sui/JsonRpcTransactionBuilderIntTests.java
@@ -18,6 +18,8 @@
import com.google.common.collect.Lists;
+import io.sui.clients.JsonRpcTransactionBuilder;
+import io.sui.clients.TransactionBuilder;
import io.sui.jsonrpc.GsonJsonHandler;
import io.sui.jsonrpc.JsonHandler;
import io.sui.jsonrpc.JsonRpcClientProvider;
diff --git a/src/integrationTest/java/io/sui/QueryClientImplIntTests.java b/src/integrationTest/java/io/sui/QueryClientImplIntTests.java
index 0c9d32d..d297127 100644
--- a/src/integrationTest/java/io/sui/QueryClientImplIntTests.java
+++ b/src/integrationTest/java/io/sui/QueryClientImplIntTests.java
@@ -18,6 +18,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+import io.sui.clients.QueryClient;
+import io.sui.clients.QueryClientImpl;
import io.sui.jsonrpc.GsonJsonHandler;
import io.sui.jsonrpc.JsonHandler;
import io.sui.jsonrpc.JsonRpc20Response.Error.ErrorCode;
@@ -317,7 +319,7 @@ void getTransactionsInRange() throws ExecutionException, InterruptedException {
@DisplayName("Test getEvents.")
void getEvents() throws ExecutionException, InterruptedException {
TransactionEventQuery query = new TransactionEventQuery();
- query.setTransaction("ov1tDrhdOqRW2uFweTbSSTaQbBbnjHWmrsh675lwb0Q=");
+ query.setTransaction("9HF7ZAfdStA8d9eUuxfKBn4V2vWcvzT8tccs4CAVrFtj");
CompletableFuture res = client.getEvents(query, null, 1, false);
System.out.println(res.get());
}
diff --git a/src/main/java/io/sui/ExecutionClient.java b/src/main/java/io/sui/clients/ExecutionClient.java
similarity index 97%
rename from src/main/java/io/sui/ExecutionClient.java
rename to src/main/java/io/sui/clients/ExecutionClient.java
index f43c4e3..41d47e2 100644
--- a/src/main/java/io/sui/ExecutionClient.java
+++ b/src/main/java/io/sui/clients/ExecutionClient.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import io.sui.models.transactions.TransactionEffects;
diff --git a/src/main/java/io/sui/ExecutionClientImpl.java b/src/main/java/io/sui/clients/ExecutionClientImpl.java
similarity index 98%
rename from src/main/java/io/sui/ExecutionClientImpl.java
rename to src/main/java/io/sui/clients/ExecutionClientImpl.java
index 55ed8a1..9876b13 100644
--- a/src/main/java/io/sui/ExecutionClientImpl.java
+++ b/src/main/java/io/sui/clients/ExecutionClientImpl.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import com.google.common.collect.Lists;
diff --git a/src/main/java/io/sui/JsonRpcTransactionBuilder.java b/src/main/java/io/sui/clients/JsonRpcTransactionBuilder.java
similarity index 99%
rename from src/main/java/io/sui/JsonRpcTransactionBuilder.java
rename to src/main/java/io/sui/clients/JsonRpcTransactionBuilder.java
index 583cd2c..53ad64d 100644
--- a/src/main/java/io/sui/JsonRpcTransactionBuilder.java
+++ b/src/main/java/io/sui/clients/JsonRpcTransactionBuilder.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import com.google.common.collect.Lists;
diff --git a/src/main/java/io/sui/QueryClient.java b/src/main/java/io/sui/clients/QueryClient.java
similarity index 99%
rename from src/main/java/io/sui/QueryClient.java
rename to src/main/java/io/sui/clients/QueryClient.java
index 0cff940..d7d11e7 100644
--- a/src/main/java/io/sui/QueryClient.java
+++ b/src/main/java/io/sui/clients/QueryClient.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import io.sui.models.CommitteeInfoResponse;
diff --git a/src/main/java/io/sui/QueryClientImpl.java b/src/main/java/io/sui/clients/QueryClientImpl.java
similarity index 99%
rename from src/main/java/io/sui/QueryClientImpl.java
rename to src/main/java/io/sui/clients/QueryClientImpl.java
index 87014fc..192556d 100644
--- a/src/main/java/io/sui/QueryClientImpl.java
+++ b/src/main/java/io/sui/clients/QueryClientImpl.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import com.google.common.collect.Lists;
diff --git a/src/main/java/io/sui/TransactionBuilder.java b/src/main/java/io/sui/clients/TransactionBuilder.java
similarity index 99%
rename from src/main/java/io/sui/TransactionBuilder.java
rename to src/main/java/io/sui/clients/TransactionBuilder.java
index 6bc1caf..0873ec5 100644
--- a/src/main/java/io/sui/TransactionBuilder.java
+++ b/src/main/java/io/sui/clients/TransactionBuilder.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import io.sui.models.transactions.TransactionBytes;
diff --git a/src/main/java/io/sui/crypto/AbstractKeyStore.java b/src/main/java/io/sui/crypto/AbstractKeyStore.java
new file mode 100644
index 0000000..6007bd1
--- /dev/null
+++ b/src/main/java/io/sui/crypto/AbstractKeyStore.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+
+import java.util.concurrent.ConcurrentSkipListMap;
+
+/**
+ * The type Abstract key store.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public abstract class AbstractKeyStore implements KeyStore {
+
+ /** The Keys. */
+ protected final ConcurrentSkipListMap> keys = new ConcurrentSkipListMap<>();
+}
diff --git a/src/main/java/io/sui/crypto/ED25519KeyPair.java b/src/main/java/io/sui/crypto/ED25519KeyPair.java
new file mode 100644
index 0000000..28eff1e
--- /dev/null
+++ b/src/main/java/io/sui/crypto/ED25519KeyPair.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.jcajce.provider.digest.SHA3.Digest256;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * The type Secp256k1 key pair.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+public class ED25519KeyPair extends SuiKeyPair {
+
+ /**
+ * Instantiates a new Ed 25519 key pair.
+ *
+ * @param privateKeyParameters the private key parameters
+ * @param publicKeyParameters the public key parameters
+ */
+ public ED25519KeyPair(
+ Ed25519PrivateKeyParameters privateKeyParameters,
+ Ed25519PublicKeyParameters publicKeyParameters) {
+ this.keyPair = new AsymmetricCipherKeyPair(publicKeyParameters, privateKeyParameters);
+ }
+
+ @Override
+ public String address() {
+ final Digest256 digest256 = new Digest256();
+ final byte[] hash =
+ digest256.digest(
+ Arrays.prepend(
+ ((Ed25519PublicKeyParameters) keyPair.getPublic()).getEncoded(),
+ SignatureScheme.ED25519.getScheme()));
+ return "0x" + StringUtils.substring(Hex.toHexString(hash), 0, 40);
+ }
+
+ /**
+ * Decode base 64 sui key pair.
+ *
+ * @param encoded the encoded
+ * @return the sui key pair
+ */
+ public static ED25519KeyPair decodeBase64(byte[] encoded) {
+ Ed25519PrivateKeyParameters privateKeyParameters =
+ new Ed25519PrivateKeyParameters(encoded, 1 + Ed25519PublicKeyParameters.KEY_SIZE);
+ Ed25519PublicKeyParameters publicKeyParameters = privateKeyParameters.generatePublicKey();
+ return new ED25519KeyPair(privateKeyParameters, publicKeyParameters);
+ }
+}
diff --git a/src/main/java/io/sui/crypto/FileBasedKeyStore.java b/src/main/java/io/sui/crypto/FileBasedKeyStore.java
new file mode 100644
index 0000000..f47174d
--- /dev/null
+++ b/src/main/java/io/sui/crypto/FileBasedKeyStore.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * The type File based key store.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public class FileBasedKeyStore extends AbstractKeyStore {
+
+ private final String path;
+
+ /**
+ * Instantiates a new File based key store.
+ *
+ * @param path the path
+ */
+ public FileBasedKeyStore(String path) {
+ this.path = path;
+ if (Files.exists(Paths.get(this.path))) {
+ try {
+ JsonArray json =
+ new Gson().fromJson(Files.newBufferedReader(Paths.get(this.path)), JsonArray.class);
+ json.asList()
+ .forEach(
+ jsonElement -> {
+ try {
+ final SuiKeyPair> keyPair =
+ SuiKeyPair.decodeBase64(jsonElement.getAsString());
+ FileBasedKeyStore.super.keys.putIfAbsent(keyPair.address(), keyPair);
+ } catch (SignatureSchemeNotSupportedException e) {
+ throw new FileBasedKeyStoreInitException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new FileBasedKeyStoreInitException(e);
+ }
+ }
+ }
+
+ /**
+ * Gets path.
+ *
+ * @return the path
+ */
+ public String getPath() {
+ return path;
+ }
+}
diff --git a/src/main/java/io/sui/crypto/FileBasedKeyStoreInitException.java b/src/main/java/io/sui/crypto/FileBasedKeyStoreInitException.java
new file mode 100644
index 0000000..8b6a441
--- /dev/null
+++ b/src/main/java/io/sui/crypto/FileBasedKeyStoreInitException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+/**
+ * The type FileBasedKeyStoreInitException.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public class FileBasedKeyStoreInitException extends RuntimeException {
+
+ /**
+ * Instantiates a new File based key store init exception.
+ *
+ * @param cause the cause
+ */
+ public FileBasedKeyStoreInitException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/io/sui/crypto/KeyStore.java b/src/main/java/io/sui/crypto/KeyStore.java
new file mode 100644
index 0000000..d60546d
--- /dev/null
+++ b/src/main/java/io/sui/crypto/KeyStore.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+/**
+ * The interface Key store.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public interface KeyStore {}
diff --git a/src/main/java/io/sui/crypto/SECP256K1KeyPair.java b/src/main/java/io/sui/crypto/SECP256K1KeyPair.java
new file mode 100644
index 0000000..08326bd
--- /dev/null
+++ b/src/main/java/io/sui/crypto/SECP256K1KeyPair.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+import static org.bouncycastle.util.Arrays.prepend;
+
+import java.util.Arrays;
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.jcajce.provider.digest.SHA3;
+import org.bouncycastle.util.encoders.Hex;
+import org.web3j.crypto.ECKeyPair;
+import org.web3j.crypto.Sign;
+
+/**
+ * The type Secp256k1 key pair.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+public class SECP256K1KeyPair extends SuiKeyPair {
+
+ /**
+ * Instantiates a new Secp 256 k 1 key pair.
+ *
+ * @param privateKey the secret key
+ */
+ public SECP256K1KeyPair(byte[] privateKey) {
+ this.keyPair = ECKeyPair.create(privateKey);
+ }
+
+ /**
+ * Decode base 64 sui key pair.
+ *
+ * @param encoded the encoded
+ * @return the sui key pair
+ */
+ public static SECP256K1KeyPair decodeBase64(byte[] encoded) {
+ final int compressedPublicKeySize = 33;
+ return new SECP256K1KeyPair(
+ Arrays.copyOfRange(encoded, 1 + compressedPublicKeySize, encoded.length));
+ }
+
+ @Override
+ public String address() {
+ SHA3.Digest256 digest256 = new SHA3.Digest256();
+ byte[] hash =
+ digest256.digest(
+ prepend(
+ Sign.publicPointFromPrivate(keyPair.getPrivateKey()).getEncoded(true),
+ SignatureScheme.Secp256k1.getScheme()));
+ return "0x" + StringUtils.substring(Hex.toHexString(hash), 0, 40);
+ }
+}
diff --git a/src/main/java/io/sui/crypto/SignatureScheme.java b/src/main/java/io/sui/crypto/SignatureScheme.java
new file mode 100644
index 0000000..2abdd58
--- /dev/null
+++ b/src/main/java/io/sui/crypto/SignatureScheme.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The enum Signature scheme.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public enum SignatureScheme {
+ /** Ed 25519 signature scheme. */
+ ED25519((byte) 0x00),
+ /** Secp 256 k 1 signature scheme. */
+ Secp256k1((byte) 0x01),
+ /** Bls 12381 signature scheme. */
+ BLS12381((byte) 0xff);
+
+ private static final Map BY_SCHEME = new HashMap<>();
+
+ static {
+ for (SignatureScheme e : values()) {
+ BY_SCHEME.put(e.scheme, e);
+ }
+ }
+
+ private final byte scheme;
+
+ SignatureScheme(byte scheme) {
+ this.scheme = scheme;
+ }
+
+ /**
+ * Gets scheme.
+ *
+ * @return the scheme
+ */
+ public byte getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Value of signature scheme.
+ *
+ * @param scheme the scheme
+ * @return the signature scheme
+ */
+ public static SignatureScheme valueOf(byte scheme) {
+ return BY_SCHEME.get(scheme);
+ }
+}
diff --git a/src/main/java/io/sui/crypto/SignatureSchemeNotSupportedException.java b/src/main/java/io/sui/crypto/SignatureSchemeNotSupportedException.java
new file mode 100644
index 0000000..4d57d72
--- /dev/null
+++ b/src/main/java/io/sui/crypto/SignatureSchemeNotSupportedException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+/**
+ * The type Signature scheme not supported exception.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+public class SignatureSchemeNotSupportedException extends Exception {
+
+ /** Instantiates a new Signature scheme not supported exception. */
+ public SignatureSchemeNotSupportedException() {
+ super("only ed25519 and secp256k1 signature scheme supported.");
+ }
+}
diff --git a/src/main/java/io/sui/crypto/SuiKeyPair.java b/src/main/java/io/sui/crypto/SuiKeyPair.java
new file mode 100644
index 0000000..7cd1ffa
--- /dev/null
+++ b/src/main/java/io/sui/crypto/SuiKeyPair.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * The type Sui key pair.
+ *
+ * @param the type parameter
+ * @author grapebaba
+ * @since 2022.11
+ */
+public abstract class SuiKeyPair {
+
+ /** The Key pair. */
+ protected T keyPair;
+
+ /**
+ * Gets key pair.
+ *
+ * @return the key pair
+ */
+ public T getKeyPair() {
+ return keyPair;
+ }
+
+ @Override
+ public String toString() {
+ return "SuiKeyPair{" + "keyPair=" + keyPair + '}';
+ }
+
+ /**
+ * Address string.
+ *
+ * @return the string
+ */
+ public abstract String address();
+
+ /**
+ * Decode base64 sui key pair.
+ *
+ * @param encoded the encoded
+ * @return the sui key pair
+ * @throws SignatureSchemeNotSupportedException the signature scheme not supported exception
+ */
+ public static SuiKeyPair> decodeBase64(String encoded)
+ throws SignatureSchemeNotSupportedException {
+ final byte[] keyPairBytes = Base64.decode(encoded);
+
+ final SignatureScheme scheme = SignatureScheme.valueOf(keyPairBytes[0]);
+ if (scheme == null) {
+ throw new SignatureSchemeNotSupportedException();
+ }
+ switch (scheme) {
+ case ED25519:
+ return ED25519KeyPair.decodeBase64(keyPairBytes);
+ case Secp256k1:
+ return SECP256K1KeyPair.decodeBase64(keyPairBytes);
+ default:
+ throw new SignatureSchemeNotSupportedException();
+ }
+ }
+}
diff --git a/src/main/java/io/sui/jsonrpc/JsonRpcClientProvider.java b/src/main/java/io/sui/jsonrpc/JsonRpcClientProvider.java
index 4a33190..1743e6c 100644
--- a/src/main/java/io/sui/jsonrpc/JsonRpcClientProvider.java
+++ b/src/main/java/io/sui/jsonrpc/JsonRpcClientProvider.java
@@ -72,9 +72,12 @@ public CompletableFuture callAndUnwrapResponse(
.thenAccept(
jsonRpc20Response -> {
if (jsonRpc20Response.getError() != null) {
- SuiApiException e = new SuiApiException(jsonRpc20Response.getError());
+ final SuiApiException e;
if (jsonRpc20Response.getThrowable() != null) {
- e.setCause(jsonRpc20Response.getThrowable());
+ e = new SuiApiException(jsonRpc20Response.getThrowable());
+ e.setError(jsonRpc20Response.getError());
+ } else {
+ e = new SuiApiException(jsonRpc20Response.getError());
}
future.completeExceptionally(e);
} else {
diff --git a/src/main/java/io/sui/models/SuiApiException.java b/src/main/java/io/sui/models/SuiApiException.java
index df144e3..99f603e 100644
--- a/src/main/java/io/sui/models/SuiApiException.java
+++ b/src/main/java/io/sui/models/SuiApiException.java
@@ -25,12 +25,10 @@
* @author grapebaba
* @since 2022.11
*/
-public class SuiApiException extends RuntimeException {
+public class SuiApiException extends Exception {
private JsonRpc20Response.Error error;
- private Throwable cause;
-
/**
* Instantiates a new Sui api exception.
*
@@ -57,13 +55,4 @@ public JsonRpc20Response.Error getError() {
public void setError(JsonRpc20Response.Error error) {
this.error = error;
}
-
- public void setCause(Throwable cause) {
- this.cause = cause;
- }
-
- @Override
- public Throwable getCause() {
- return cause;
- }
}
diff --git a/src/test/java/io/sui/ExecutionClientImplTest.java b/src/test/java/io/sui/clients/ExecutionClientImplTest.java
similarity index 99%
rename from src/test/java/io/sui/ExecutionClientImplTest.java
rename to src/test/java/io/sui/clients/ExecutionClientImplTest.java
index fd0a744..f317095 100644
--- a/src/test/java/io/sui/ExecutionClientImplTest.java
+++ b/src/test/java/io/sui/clients/ExecutionClientImplTest.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import static io.sui.models.transactions.ExecutionStatus.ExecutionStatusType.success;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/src/test/java/io/sui/JsonRpcTransactionBuilderTests.java b/src/test/java/io/sui/clients/JsonRpcTransactionBuilderTests.java
similarity index 99%
rename from src/test/java/io/sui/JsonRpcTransactionBuilderTests.java
rename to src/test/java/io/sui/clients/JsonRpcTransactionBuilderTests.java
index 1e8bebd..bd6c122 100644
--- a/src/test/java/io/sui/JsonRpcTransactionBuilderTests.java
+++ b/src/test/java/io/sui/clients/JsonRpcTransactionBuilderTests.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/src/test/java/io/sui/QueryClientImplTests.java b/src/test/java/io/sui/clients/QueryClientImplTests.java
similarity index 99%
rename from src/test/java/io/sui/QueryClientImplTests.java
rename to src/test/java/io/sui/clients/QueryClientImplTests.java
index 73edf16..ae4e611 100644
--- a/src/test/java/io/sui/QueryClientImplTests.java
+++ b/src/test/java/io/sui/clients/QueryClientImplTests.java
@@ -14,7 +14,7 @@
* specific language governing permissions and limitations under the License.
*/
-package io.sui;
+package io.sui.clients;
import static io.sui.models.objects.MoveFunctionArgType.ObjectValueKindMoveFunctionArgType.ObjectValueKind.ByMutableReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/src/test/java/io/sui/crypto/ED25519KeyPairTest.java b/src/test/java/io/sui/crypto/ED25519KeyPairTest.java
new file mode 100644
index 0000000..904ae9c
--- /dev/null
+++ b/src/test/java/io/sui/crypto/ED25519KeyPairTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.charset.StandardCharsets;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+import org.bouncycastle.jcajce.provider.digest.SHA3;
+import org.bouncycastle.jcajce.provider.digest.SHA3.Digest256;
+import org.bouncycastle.util.encoders.Base64;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The type Ed25519 key pair test.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+class ED25519KeyPairTest {
+
+ /** Address. */
+ @Test
+ void address() {
+ final String base64 =
+ "AOSqUUDaiPGYESoI/G13YwT2qyWW/RRvsW2G7IoCG"
+ + "URYYbRLmbwt4NZ9m9x7s8taYhCJg9OQdkrmSTVioUVpCSM=";
+ final ED25519KeyPair keyPair = ED25519KeyPair.decodeBase64(Base64.decode(base64));
+
+ assertEquals("0x1e7752f22228753e5745f5ac8ad4ef1bbc502845", keyPair.address());
+ }
+
+ /** Decode base 64. */
+ @Test
+ void decodeBase64() throws CryptoException {
+ final String base64 =
+ "AOSqUUDaiPGYESoI/G13YwT2qyWW/RRvsW2G7IoCG"
+ + "URYYbRLmbwt4NZ9m9x7s8taYhCJg9OQdkrmSTVioUVpCSM=";
+ final ED25519KeyPair ed25519KeyPair = ED25519KeyPair.decodeBase64(Base64.decode(base64));
+
+ Signer signer = new Ed25519Signer();
+ signer.init(true, ed25519KeyPair.getKeyPair().getPrivate());
+ final String msg = "test";
+ final SHA3.Digest256 digest = new Digest256();
+ final byte[] encodedHash = digest.digest(msg.getBytes(StandardCharsets.UTF_8));
+ signer.update(encodedHash, 0, encodedHash.length);
+ byte[] signature = signer.generateSignature();
+
+ Signer verifier = new Ed25519Signer();
+ verifier.init(false, ed25519KeyPair.getKeyPair().getPublic());
+ verifier.update(encodedHash, 0, encodedHash.length);
+ assertTrue(verifier.verifySignature(signature));
+ }
+}
diff --git a/src/test/java/io/sui/crypto/FileBasedKeyStoreTest.java b/src/test/java/io/sui/crypto/FileBasedKeyStoreTest.java
new file mode 100644
index 0000000..aa0c1ca
--- /dev/null
+++ b/src/test/java/io/sui/crypto/FileBasedKeyStoreTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Paths;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The type File based key store test.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+class FileBasedKeyStoreTest {
+
+ /** Gets path. */
+ @Test
+ void getPath() {
+ FileBasedKeyStore fileBasedKeyStore =
+ new FileBasedKeyStore(
+ Paths.get("src/test/resources/config/sui.keystore").toAbsolutePath().toString());
+ System.out.println(fileBasedKeyStore.getPath());
+ assertTrue(StringUtils.endsWith(fileBasedKeyStore.getPath(), "config/sui.keystore"));
+ }
+
+ /** Init key pairs. */
+ @Test
+ void initKeyPairs() {
+ FileBasedKeyStore fileBasedKeyStore =
+ new FileBasedKeyStore(
+ Paths.get("src/test/resources/config/sui.keystore").toAbsolutePath().toString());
+
+ assertEquals(11, fileBasedKeyStore.keys.size());
+ String expected =
+ "0x1e7752f22228753e5745f5ac8ad4ef1bbc502845\n"
+ + "0x207f2c9f08472b1ff68644fdfc7a70df10cb3d4e\n"
+ + "0x49ef9b602b76a37e0f9177783755c1a190866e72\n"
+ + "0x51972acc644b8c6dd81d6088780b40e842a0a10c\n"
+ + "0x51de405091c9f971fc6085d384f9ba764f268fca\n"
+ + "0x63485e00efc944d62349b79f88a11b7cacc2a764\n"
+ + "0x78cec6011e9d0690d5fbbfa4d25987a087a88ee7\n"
+ + "0x887ddfbf2bc37d757eabb08d62bf725a04922bde\n"
+ + "0xca21af1b5b347d315d7355ff9e6e73cc79d0a4d0\n"
+ + "0xe8da3f038048e2cd6339e916a926874d0d0604b7\n"
+ + "0xea79464d86786b7a7a63e3f13f798f29f5e65947\n";
+ StringBuilder actual = new StringBuilder();
+ for (String key : fileBasedKeyStore.keys.navigableKeySet()) {
+ actual.append(key).append("\n");
+ }
+ assertEquals(expected, actual.toString());
+ }
+}
diff --git a/src/test/java/io/sui/crypto/SECP256K1KeyPairTest.java b/src/test/java/io/sui/crypto/SECP256K1KeyPairTest.java
new file mode 100644
index 0000000..febd146
--- /dev/null
+++ b/src/test/java/io/sui/crypto/SECP256K1KeyPairTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+import org.bouncycastle.jcajce.provider.digest.SHA3;
+import org.bouncycastle.jcajce.provider.digest.SHA3.Digest256;
+import org.bouncycastle.util.encoders.Base64;
+import org.junit.jupiter.api.Test;
+import org.web3j.crypto.ECKeyPair;
+import org.web3j.crypto.Sign;
+import org.web3j.crypto.Sign.SignatureData;
+
+/**
+ * The type Secp256k1 key pair test.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+class SECP256K1KeyPairTest {
+
+ /**
+ * Address.
+ *
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ */
+ @Test
+ void address() throws NoSuchAlgorithmException {
+ final String base64 =
+ "AQLE7fDdDt4nrbGgCX8umsFscJRFY4t3Bkrk3MaB"
+ + "b1nnA6dD5QHIFrPAdPQtdDyfoJNjiN/ghxuVLxfHxehcwec0";
+ final SuiKeyPair secp256K1KeyPair =
+ SECP256K1KeyPair.decodeBase64(Base64.decode(base64));
+
+ assertEquals("0xe8da3f038048e2cd6339e916a926874d0d0604b7", secp256K1KeyPair.address());
+ }
+
+ /**
+ * Decode base 64.
+ *
+ * @throws SignatureException the signature exception
+ */
+ @Test
+ void decodeBase64() throws SignatureException {
+ final String base64 =
+ "AQLE7fDdDt4nrbGgCX8umsFscJRFY4t3Bkrk3MaBb1nnA6dD5QH"
+ + "IFrPAdPQtdDyfoJNjiN/ghxuVLxfHxehcwec0";
+ final SuiKeyPair secp256K1KeyPair =
+ SECP256K1KeyPair.decodeBase64(Base64.decode(base64));
+ final String msg = "test";
+ final SHA3.Digest256 digest = new Digest256();
+ final byte[] encodedHash = digest.digest(msg.getBytes(StandardCharsets.UTF_8));
+ final SignatureData signatureData =
+ Sign.signMessage(encodedHash, secp256K1KeyPair.keyPair, false);
+ BigInteger pubKeyRecovered = Sign.signedMessageHashToKey(encodedHash, signatureData);
+ assertEquals(secp256K1KeyPair.getKeyPair().getPublicKey(), pubKeyRecovered);
+ }
+}
diff --git a/src/test/java/io/sui/crypto/SuiKeyPairTest.java b/src/test/java/io/sui/crypto/SuiKeyPairTest.java
new file mode 100644
index 0000000..df77a23
--- /dev/null
+++ b/src/test/java/io/sui/crypto/SuiKeyPairTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 281165273grape@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package io.sui.crypto;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.nio.charset.StandardCharsets;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The type Sui key pair test.
+ *
+ * @author grapebaba
+ * @since 2022.11
+ */
+class SuiKeyPairTest {
+
+ /** Decode base 64. */
+ @Test
+ void decodeBase64() {
+ final String base64 =
+ "AQLE7fDdDt4nrbGgCX8umsFscJRFY4t3Bkrk3MaB"
+ + "b1nnA6dD5QHIFrPAdPQtdDyfoJNjiN/ghxuVLxfHxehcwec0";
+ try {
+ SuiKeyPair.decodeBase64(base64);
+ } catch (SignatureSchemeNotSupportedException e) {
+ e.printStackTrace();
+ }
+
+ final String wrongBase64 = Base64.toBase64String("test".getBytes(StandardCharsets.UTF_8));
+ assertThrows(
+ SignatureSchemeNotSupportedException.class, () -> SuiKeyPair.decodeBase64(wrongBase64));
+
+ final String blsBase64 =
+ Base64.toBase64String(Arrays.prepend("test".getBytes(StandardCharsets.UTF_8), (byte) 0xff));
+ assertThrows(
+ SignatureSchemeNotSupportedException.class, () -> SuiKeyPair.decodeBase64(blsBase64));
+ }
+}
diff --git a/src/test/resources/config/sui.keystore b/src/test/resources/config/sui.keystore
new file mode 100644
index 0000000..e745c28
--- /dev/null
+++ b/src/test/resources/config/sui.keystore
@@ -0,0 +1,13 @@
+[
+ "AOSqUUDaiPGYESoI/G13YwT2qyWW/RRvsW2G7IoCGURYYbRLmbwt4NZ9m9x7s8taYhCJg9OQdkrmSTVioUVpCSM=",
+ "AQOn0iLM/e8juxoMI4V1Usun+6ohvRqH5JBZ6gOV4bFW89iK30AcgeoKuTw6ftEdYeHtuIo+cJ9+nh9iV5QsjwHz",
+ "APuC5Q1OqLitEra+foDkhcuNCw5QXl3+SWyJfgN6W/UvWnP2cVdopkxK/yhW2s+R3YGMiukPURMUX4q5mKfGTyk=",
+ "ACQKog3HkkIWw7/hGXekeyc/oLUua2H7JZwhjXgycpxggZoUkSgyKt2MTevSG9PB5aZLN84cvgwMqzxpDtkFDJY=",
+ "AJty6phE36GqKobHqHGOtplnLXQ6jq1AVaiiX7XkDuA7ZRbMX0ps9KUnAm3mKO4vXR+El4vdf7qk1FYMK3PAIaY=",
+ "AGkJtIyKHM0hf528r/PxwwK7oKEDgXXtv+AGO1qaVfNxVN9ABp/RbUimEybWK1o0pY3JpEUnIve/6v1sHOLzfik=",
+ "AQLeapMz9pSCMPHXOf+fl7snw6O5wgz/WQgLM+Rmzh7BFWlfRE6/2ROHOa6K88gdMRdsAAI7nP/1TRJqhuasG4nW",
+ "AFjviUpW2gMO7bs9D7lRKO1zlmjNTpvbZJXdNUZhizPFkL+LtkqeZuDLV+GmDb9IP2L3pS6J19uz24r8aoFj//Q=",
+ "AF7bpdmbv/s7zvt/vjps6lLj/4bkz9FzGeXzmjYHAfz8hdarcmYgoQvtlCS4EtEAR2AUX9g7HDuwlxs1HbXTlEU=",
+ "AQLE7fDdDt4nrbGgCX8umsFscJRFY4t3Bkrk3MaBb1nnA6dD5QHIFrPAdPQtdDyfoJNjiN/ghxuVLxfHxehcwec0",
+ "AJ/On29cDTJ1yHMIJV2Wj87LMPHNxD+IIOsa498SQcmIGTayUd4nokl1C9qFhtCXatrzKUSEIHX7l0dZeAO2TVs="
+]
\ No newline at end of file