From 81283a29b70d6848785ff420ac2a610e40a0fae5 Mon Sep 17 00:00:00 2001 From: Tokenview <40224239+Tokenview@users.noreply.github.com> Date: Sat, 6 Mar 2021 15:01:25 +0800 Subject: [PATCH] Add files via upload --- LICENSE | 21 + README.md | 28 ++ api-demo.iml | 92 +++++ api-java-sdk/api-java-sdk.iml | 93 +++++ api-java-sdk/pom.xml | 173 ++++++++ .../com/tokenview/api/client/APIClient.java | 86 ++++ .../tokenview/api/client/APICredentials.java | 30 ++ .../tokenview/api/client/APIHttpClient.java | 90 +++++ .../com/tokenview/api/client/APIRetrofit.java | 29 ++ .../com/tokenview/api/client/ApiHttp.java | 38 ++ .../api/config/APIConfiguration.java | 126 ++++++ .../tokenview/api/constant/APIConstants.java | 83 ++++ .../com/tokenview/api/enums/CharsetEnum.java | 24 ++ .../tokenview/api/enums/ContentTypeEnum.java | 26 ++ .../com/tokenview/api/enums/I18nEnum.java | 24 ++ .../com/tokenview/api/enums/ResultCode.java | 111 ++++++ .../tokenview/api/exception/APIException.java | 31 ++ .../com/tokenview/api/result/HttpResult.java | 70 ++++ .../api/service/base/BaseService.java | 25 ++ .../api/service/base/Impl/BaseAPI.java | 29 ++ .../service/base/Impl/BaseServiceImpl.java | 47 +++ .../api/service/wallet/WalletService.java | 16 + .../api/service/wallet/impl/WalletAPI.java | 20 + .../wallet/impl/WalletServiceImpl.java | 36 ++ .../tokenview/api/test/base/BaseAPITest.java | 99 +++++ .../tokenview/api/test/base/BaseConfig.java | 29 ++ .../api/test/services/ServicesAPITest.java | 11 + .../api/test/services/ServicesConfig.java | 30 ++ .../api/test/wallet/WalletAPITest.java | 187 +++++++++ .../api/test/wallet/WalletConfig.java | 29 ++ pom.xml | 17 + sign-java-sdk/pom.xml | 210 ++++++++++ sign-java-sdk/sign-java-sdk.iml | 91 +++++ .../java/com/tokenview/bean/WalletBean.java | 67 ++++ .../java/com/tokenview/enums/Version.java | 15 + .../java/com/tokenview/sign/btc/BTCSign.java | 59 +++ .../java/com/tokenview/sign/eth/ETHSign.java | 59 +++ .../com/tokenview/utils/BTCWalletUtil.java | 304 ++++++++++++++ .../java/com/tokenview/utils/HexUtil.java | 80 ++++ .../com/tokenview/utils/HttpClientUtils.java | 370 ++++++++++++++++++ .../java/com/tokenview/utils/HttpUtil.java | 55 +++ .../com/tokenview/sign/test/BTCSignTest.java | 76 ++++ .../com/tokenview/sign/test/ETHSignTest.java | 51 +++ 43 files changed, 3187 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 api-demo.iml create mode 100644 api-java-sdk/api-java-sdk.iml create mode 100644 api-java-sdk/pom.xml create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/client/APIClient.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/client/APICredentials.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/client/APIHttpClient.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/client/APIRetrofit.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/client/ApiHttp.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/config/APIConfiguration.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/constant/APIConstants.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/enums/CharsetEnum.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/enums/ContentTypeEnum.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/enums/I18nEnum.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/enums/ResultCode.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/exception/APIException.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/result/HttpResult.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/base/BaseService.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseAPI.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseServiceImpl.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/wallet/WalletService.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletAPI.java create mode 100644 api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletServiceImpl.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseAPITest.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseConfig.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesAPITest.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesConfig.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletAPITest.java create mode 100644 api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletConfig.java create mode 100644 pom.xml create mode 100644 sign-java-sdk/pom.xml create mode 100644 sign-java-sdk/sign-java-sdk.iml create mode 100644 sign-java-sdk/src/main/java/com/tokenview/bean/WalletBean.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/enums/Version.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/sign/btc/BTCSign.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/sign/eth/ETHSign.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/utils/BTCWalletUtil.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/utils/HexUtil.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/utils/HttpClientUtils.java create mode 100644 sign-java-sdk/src/main/java/com/tokenview/utils/HttpUtil.java create mode 100644 sign-java-sdk/src/test/java/com/tokenview/sign/test/BTCSignTest.java create mode 100644 sign-java-sdk/src/test/java/com/tokenview/sign/test/ETHSignTest.java diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..10bc648 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 tokenview-hub + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..45547d1 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +The Tokenview api-demo Project +=== +* Tokenview signature and api program implementation in Java. + +Features +--- +* 120+ blockchains ,100B+ transactions ,20B+ addresses ,coin markets ,statistical information are supported +* Demo code are mainly used for wallet app now,private key is kept by the developer to prevent leakage during transmission +* Main crypto currency offline signature is supported +* Transaction-broadcasting is supported +* Fully Java ready ,with definition files and full Java source and Springboot2.0 +* MIT License (including ALL dependencies) ;completely open source to do with as you please + +Launch your application +--- +* Get apikey here :https://services.tokenview.com +* Download api-demo project and check how the tokenview API is called in the test class of this project ,take it as a prototype extension, modify it into the project you want, package it and add it to your application project +* Check postman document and start your application :https://documenter.getpostman.com/view/5728777/RzZ6HfX2 + +Documentation +--- +* Official site :https://services.tokenview.com +* Browser site :https://tokenview.com +* Postman document :https://documenter.getpostman.com/view/5728777/RzZ6HfX2 + +License +--- +* MIT License (including all dependencies). diff --git a/api-demo.iml b/api-demo.iml new file mode 100644 index 0000000..666f4ed --- /dev/null +++ b/api-demo.iml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-java-sdk/api-java-sdk.iml b/api-java-sdk/api-java-sdk.iml new file mode 100644 index 0000000..1d56643 --- /dev/null +++ b/api-java-sdk/api-java-sdk.iml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-java-sdk/pom.xml b/api-java-sdk/pom.xml new file mode 100644 index 0000000..1ade63b --- /dev/null +++ b/api-java-sdk/pom.xml @@ -0,0 +1,173 @@ + + + + api-demo + com.tokenview + 1.0.0 + + 4.0.0 + + com.tokenview + api-java-sdk + 1.0.0 + + + + com.tokenview + sign-java-sdk + 1.0.0 + + + io.netty + netty-all + 4.1.36.Final + + + com.squareup.okhttp3 + okhttp + 3.10.0 + + + com.squareup.retrofit2 + retrofit + 2.3.0 + + + org.slf4j + slf4j-log4j12 + 1.8.0-beta2 + + + org.springframework + spring-context + 5.1.5.RELEASE + + + com.squareup.retrofit2 + converter-scalars + 2.3.0 + + + com.squareup.retrofit2 + converter-gson + 2.3.0 + + + com.squareup.retrofit2 + adapter-rxjava + 2.3.0 + + + org.apache.commons + commons-lang3 + 3.7 + + + commons-collections + commons-collections + 3.2.2 + + + ch.qos.logback + logback-classic + 1.2.3 + + + com.google.guava + guava + 27.1-jre + + + commons-codec + commons-codec + 1.12 + + + org.apache.commons + commons-compress + 1.18 + + + junit + junit + 4.12 + test + + + org.projectlombok + lombok + 1.18.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.5 + + + commons-beanutils + commons-beanutils + 1.9.3 + + + commons-collections + commons-collections + 3.2.1 + + + commons-lang + commons-lang + 2.6 + + + commons-logging + commons-logging + 1.1.1 + + + net.sf.ezmorph + ezmorph + 1.0.6 + + + net.sf.json-lib + json-lib + 2.2.3 + jdk15 + + + org.springframework + spring-web + 5.0.9.RELEASE + test + + + jstl + jstl + 1.2 + test + + + com.alibaba + fastjson + 1.2.12 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + \ No newline at end of file diff --git a/api-java-sdk/src/main/java/com/tokenview/api/client/APIClient.java b/api-java-sdk/src/main/java/com/tokenview/api/client/APIClient.java new file mode 100644 index 0000000..7a545a7 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/client/APIClient.java @@ -0,0 +1,86 @@ +package com.tokenview.api.client; + +import com.alibaba.fastjson.JSON; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.constant.APIConstants; +import com.tokenview.api.exception.APIException; +import com.tokenview.api.result.HttpResult; +import okhttp3.OkHttpClient; +import org.apache.commons.lang.StringUtils; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import java.io.IOException; + + +/** + * TokenView API Client + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/20 11:13 + */ +public class APIClient { + + private final APIConfiguration config; + private final APICredentials credentials; + private final OkHttpClient client; + private final Retrofit retrofit; + private final ApiHttp apiHttp; + /** + * Initialize the apis client + */ + public APIClient(final APIConfiguration config) { + if (config == null || StringUtils.isEmpty(config.getEndpoint())) { + throw new RuntimeException("The APIClient params can't be empty."); + } + this.config = config; + this.credentials = new APICredentials(config); + this.client = new APIHttpClient(config, this.credentials).client(); + this.retrofit = new APIRetrofit(config, this.client).retrofit(); + this.apiHttp = new ApiHttp(config, this.client); + } + + /** + * Initialize the retrofit operation service + */ + public T createService(final Class service) { + return this.retrofit.create(service); + } + + public ApiHttp getApiHttp() { + return this.apiHttp; + } + + /** + * Synchronous send request + */ + //解析 + public T executeSync(final Call call){ + try { + + final Response response = call.execute(); + + //获取状态码 + final int status = response.code(); + //获取错误信息 + final String message = new StringBuilder().append(response.code()).append(" / ").append(response.message()).toString(); + //响应成功 + if (response.isSuccessful()) { + return response.body(); + } else if (APIConstants.resultStatusArray.contains(status)) { + final HttpResult result = JSON.parseObject(new String(response.errorBody().bytes()), HttpResult.class); + if(result.getCode() == 0 && result.getMessage() == null){ + throw new APIException(result.getErrorCode(),result.getErrorMessage()); + }else{ + throw new APIException(result.getCode(), result.getMessage()); + } + } else { + throw new APIException(message); + } + } catch (final IOException e) { + throw new APIException("APIClient executeSync exception.", e); + } + } + +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/client/APICredentials.java b/api-java-sdk/src/main/java/com/tokenview/api/client/APICredentials.java new file mode 100644 index 0000000..6968538 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/client/APICredentials.java @@ -0,0 +1,30 @@ +package com.tokenview.api.client; + +import com.tokenview.api.config.APIConfiguration; + +/** + * API Credentials + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/18 12:24 + */ +public class APICredentials { + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + /** + * The user's secret key provided by TokenView. + */ + private String apiKey; + + public APICredentials(APIConfiguration config) { + super(); + this.apiKey = config.getApiKey(); + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/client/APIHttpClient.java b/api-java-sdk/src/main/java/com/tokenview/api/client/APIHttpClient.java new file mode 100644 index 0000000..6a182c2 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/client/APIHttpClient.java @@ -0,0 +1,90 @@ +package com.tokenview.api.client; + +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.constant.APIConstants; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okio.Buffer; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +public class APIHttpClient { + private final APIConfiguration config; + private final APICredentials credentials; + + + public APIHttpClient(final APIConfiguration config, final APICredentials credentials) { + this.config = config; + this.credentials = credentials; + } + + public OkHttpClient client() { + final OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + clientBuilder.connectTimeout(this.config.getConnectTimeout(), TimeUnit.SECONDS); + clientBuilder.readTimeout(this.config.getReadTimeout(), TimeUnit.SECONDS); + clientBuilder.writeTimeout(this.config.getWriteTimeout(), TimeUnit.SECONDS); + clientBuilder.retryOnConnectionFailure(this.config.isRetryOnConnectionFailure()); +// clientBuilder.addInterceptor((Interceptor.Chain chain) -> { +// final Request.Builder requestBuilder = chain.request().newBuilder(); +// final String timestamp = DateUtils.getUnixTime(); +// //打印首行时间戳 +//// System.out.println("时间戳timestamp={" + timestamp + "}"); +//// 设置模拟盘请求头 +//// String simulated = "1"; +// requestBuilder.headers(this.headers(chain.request(), timestamp)); +// final Request request = requestBuilder.build(); +// if (this.config.isPrint()) { +// this.printRequest(request, timestamp); +// } +// return chain.proceed(request); +// }); + return clientBuilder.build(); + } + + //返回请求路径url + private String url(final Request request) { + return request.url().toString(); + } + //将请求方法转变为大写,并返回 + private String method(final Request request) { + return request.method().toUpperCase(); + } + //返回请求路径 + private String requestPath(final Request request) { + String url = this.url(request); + url = url.replace(this.config.getEndpoint(), APIConstants.EMPTY); + String requestPath = url; + if (requestPath.contains(APIConstants.QUESTION)) { + requestPath = requestPath.substring(0, url.lastIndexOf(APIConstants.QUESTION)); + } + if(this.config.getEndpoint().endsWith(APIConstants.SLASH)){ + requestPath = APIConstants.SLASH + requestPath; + } + return requestPath; + } + + private String queryString(final Request request) { + final String url = this.url(request); + request.body(); + //请求参数为空字符串 + String queryString = APIConstants.EMPTY; + //如果URL中包含?即存在参数的拼接 + if (url.contains(APIConstants.QUESTION)) { + queryString = url.substring(url.lastIndexOf(APIConstants.QUESTION) + 1); + } + return queryString; + } + + private String body(final Request request) throws IOException { + final RequestBody requestBody = request.body(); + String body = APIConstants.EMPTY; + if (requestBody != null) { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + body = buffer.readString(APIConstants.UTF_8); + } + return body; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/client/APIRetrofit.java b/api-java-sdk/src/main/java/com/tokenview/api/client/APIRetrofit.java new file mode 100644 index 0000000..d5adc33 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/client/APIRetrofit.java @@ -0,0 +1,29 @@ +package com.tokenview.api.client; + + +import com.tokenview.api.config.APIConfiguration; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; +import retrofit2.converter.scalars.ScalarsConverterFactory; + +public class APIRetrofit { + private APIConfiguration config; + private OkHttpClient client; + + public APIRetrofit(APIConfiguration config, OkHttpClient client) { + this.config = config; + this.client = client; + } + + public Retrofit retrofit() { + Retrofit.Builder builder = new Retrofit.Builder(); + builder.client(this.client); + builder.addConverterFactory(ScalarsConverterFactory.create()); + builder.addConverterFactory(GsonConverterFactory.create()); + builder.addCallAdapterFactory(RxJavaCallAdapterFactory.create()); + builder.baseUrl(this.config.getEndpoint()); + return builder.build(); + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/client/ApiHttp.java b/api-java-sdk/src/main/java/com/tokenview/api/client/ApiHttp.java new file mode 100644 index 0000000..0a7b85f --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/client/ApiHttp.java @@ -0,0 +1,38 @@ +package com.tokenview.api.client; + +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.constant.APIConstants; +import com.tokenview.api.exception.APIException; +import okhttp3.*; + +import java.io.IOException; +import java.util.Date; + +public class ApiHttp { + private OkHttpClient client; + private APIConfiguration config; + + static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + + public ApiHttp(APIConfiguration config, OkHttpClient client) { + this.config = config; + this.client = client; + } + + private void printResponse(int status, String message, String body, boolean responseIsNotNull) { + StringBuilder responseInfo = new StringBuilder(); + responseInfo.append("\n\tResponse").append("(").append(new Date()).append("):"); + if (responseIsNotNull) { + responseInfo.append("\n\t\t").append("Status: ").append(status); + responseInfo.append("\n\t\t").append("Message: ").append(message); + responseInfo.append("\n\t\t").append("Response Body: ").append(body); + } else { + responseInfo.append("\n\t\t").append("\n\tRequest Error: response is null"); + } + } + + public String url(String url) { + return new StringBuilder(this.config.getEndpoint()).append(url).toString(); + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/config/APIConfiguration.java b/api-java-sdk/src/main/java/com/tokenview/api/config/APIConfiguration.java new file mode 100644 index 0000000..094b12c --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/config/APIConfiguration.java @@ -0,0 +1,126 @@ +package com.tokenview.api.config; + +import com.tokenview.api.constant.APIConstants; +import com.tokenview.api.enums.I18nEnum; + +/** + * API configuration information + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/20 14:29 + */ +public class APIConfiguration { + + /** + * The user's api key provided by TokenView. + */ + private String apiKey; + /** + * Rest api endpoint url. + */ + private String endpoint; + /** + * Host connection timeout. + */ + private long connectTimeout; + /** + * The host reads the information timeout. + */ + private long readTimeout; + /** + * The host writes the information timeout. + */ + private long writeTimeout; + /** + * Failure reconnection, default true. + */ + private boolean retryOnConnectionFailure; + /** + * The print api information. + */ + private boolean print; + /** + * I18nEnum + */ + private I18nEnum i18n; + + public APIConfiguration() { + this(null); + } + public APIConfiguration(String endpoint) { + super(); + this.apiKey = null; + this.endpoint = endpoint; + this.connectTimeout = APIConstants.TIMEOUT; + this.readTimeout = APIConstants.TIMEOUT; + this.writeTimeout = APIConstants.TIMEOUT; + this.retryOnConnectionFailure = true; + this.print = false; + this.i18n = I18nEnum.ENGLISH; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public long getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(long connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public long getReadTimeout() { + return readTimeout; + } + + public void setReadTimeout(long readTimeout) { + this.readTimeout = readTimeout; + } + + public long getWriteTimeout() { + return writeTimeout; + } + + public void setWriteTimeout(long writeTimeout) { + this.writeTimeout = writeTimeout; + } + + public boolean isRetryOnConnectionFailure() { + return retryOnConnectionFailure; + } + + public void setRetryOnConnectionFailure(boolean retryOnConnectionFailure) { + this.retryOnConnectionFailure = retryOnConnectionFailure; + } + + public boolean isPrint() { + return print; + } + + public void setPrint(boolean print) { + this.print = print; + } + + public I18nEnum getI18n() { + return i18n; + } + + public void setI18n(I18nEnum i18n) { + this.i18n = i18n; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/constant/APIConstants.java b/api-java-sdk/src/main/java/com/tokenview/api/constant/APIConstants.java new file mode 100644 index 0000000..fbf904f --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/constant/APIConstants.java @@ -0,0 +1,83 @@ +package com.tokenview.api.constant; + +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.enums.ContentTypeEnum; +import okhttp3.MediaType; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; + +public class APIConstants { + /** + * The default timeout is 30 seconds. + */ + public static final long TIMEOUT = 1000 * 30; + /** + * All requests and responses are application/json content type and follow typical HTTP response status codes for success and failure. + */ + public static final String CONTENT_TYPE = "Content-Type"; + + public static final String ACCEPT = "Accept"; + + public static final String COOKIE = "Cookie"; + + public static final String LOCALE = "locale="; + + public static final MediaType JSON = MediaType.parse(ContentTypeEnum.APPLICATION_JSON.contentType()); + + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + public static final String QUESTION = "?"; + + public static final String EMPTY = ""; + + public static final JSONObject NOTHING = new JSONObject(); + + public static final List toStringTypeArray = Arrays.asList( + "class java.lang.Long", + "class java.lang.Integer", + "long", + "int"); + public static final List toStringTypeDoubleArray = Arrays.asList( + "class java.lang.Double", + "double"); + + public static final List resultStatusArray = Arrays.asList( + 400,401,429,500); + + public static final String BOOLEAN = "boolean"; + public static final String IS = "is"; + public static final String get = "get"; + public static final char a = 'a'; + public static final char z = 'z'; + public static final String ZERO_STRING = "0"; + public static final String DOUBLE_ZERO_STRING = "0.00"; + + public static final String DOT1 = "."; + public static final String DOT2 = "\\."; + public static final String E = "E"; + public static final String e = "e"; + public static final int DEFAULT_SCALE = 2; + /** + * 8900000000.000000000 + */ + public static final String DOUBLE_END1 = "0+?$"; + /** + * 8900000000. + */ + public static final String DOUBLE_END2 = "[.]$"; + + /** + * default cursor id + */ + public static final int ONE = 1; + /** + * max limit: 100 + */ + public static final int HUNDRED = 100; + + public static final String HLINE = "-"; + + public static final String SLASH = "/"; +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/enums/CharsetEnum.java b/api-java-sdk/src/main/java/com/tokenview/api/enums/CharsetEnum.java new file mode 100644 index 0000000..8aae77c --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/enums/CharsetEnum.java @@ -0,0 +1,24 @@ +package com.tokenview.api.enums; + +/** + * + * @author Moilk + * @version 1.0.0 + * @date 2020/1/18 13:00 + */ +public enum CharsetEnum { + + UTF_8("UTF-8"), + ISO_8859_1("ISO-8859-1"),; + + + private String charset; + + CharsetEnum(String charset) { + this.charset = charset; + } + + public String charset() { + return charset; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/enums/ContentTypeEnum.java b/api-java-sdk/src/main/java/com/tokenview/api/enums/ContentTypeEnum.java new file mode 100644 index 0000000..bfb3422 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/enums/ContentTypeEnum.java @@ -0,0 +1,26 @@ +package com.tokenview.api.enums; + +/** + * + * @author Moilk + * @version 1.0.0 + * @date 2020/1/18 13:00 + */ +public enum ContentTypeEnum { + + APPLICATION_JSON("application/json"), + APPLICATION_JSON_UTF8("application/json; charset=UTF-8"), + // The server does not support types + APPLICATION_FORM("application/x-www-form-urlencoded; charset=UTF-8"),; + + + private String contentType; + + ContentTypeEnum(String contentType) { + this.contentType = contentType; + } + + public String contentType() { + return contentType; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/enums/I18nEnum.java b/api-java-sdk/src/main/java/com/tokenview/api/enums/I18nEnum.java new file mode 100644 index 0000000..f302eca --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/enums/I18nEnum.java @@ -0,0 +1,24 @@ +package com.tokenview.api.enums; + +/** + * I18nEnum + * + * @author Moilk + * @version 1.0.0 + * @date 2020/1/18 13:00 + */ +public enum I18nEnum { + ENGLISH("en_US"), + SIMPLIFIED_CHINESE("zh_CN"), + TRADITIONAL_CHINESE("zh_HK"),; + + private String i18n; + + I18nEnum(String i18n) { + this.i18n = i18n; + } + + public String i18n() { + return i18n; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/enums/ResultCode.java b/api-java-sdk/src/main/java/com/tokenview/api/enums/ResultCode.java new file mode 100644 index 0000000..710bb13 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/enums/ResultCode.java @@ -0,0 +1,111 @@ +package com.tokenview.api.enums; + +import java.util.ArrayList; +import java.util.List; + +/** + * I18nEnum + * + * @author Moilk + * @version 1.0.0 + * @date 2020/1/18 13:00 + */ +public enum ResultCode { + + /* 成功状态码 */ + SUCCESS(1, "成功"), + NO_DATA(404, "无数据"), + + /* 参数错误:10001-19999 */ + PARAM_IS_INVALID(10001, "参数无效"), + PARAM_IS_BLANK(10002, "参数为空"), + PARAM_TYPE_BIND_ERROR(10003, "参数类型错误"), + PARAM_NOT_COMPLETE(10004, "参数缺失"), + + /* 用户中心错误:20001-29999*/ + USER_NOT_LOGGED_IN(20001, "用户未登录"), + USER_LOGIN_ERROR(20002, "账号不存在或密码错误"), + USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"), + USER_NOT_EXIST(20004, "用户不存在"), + USER_HAS_EXISTED(20005, "用户已存在"), + USER_COLLECTION_EXISTED(20006, "用户收藏已存在"), + USER_COLLECTION_SAVE_ERROR(20007, "用户收藏更新失败"), + USER_COLLECTION_DEL_ERROR(20008, "用户收藏删除失败"), + USER_COLLECTION_LOOK_ERROR(20008, "用户收藏查询失败"), + + /* 业务错误:30001-39999 */ + //SPECIFIED_QUESTIONED_USER_NOT_EXIST(30001, "某业务出现问题"), + + /* 系统错误:40001-49999 */ + SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"), + + /* 数据错误:50001-599999 */ + RESULE_DATA_NONE(50001, "数据未找到"), + DATA_IS_WRONG(50002, "数据有误"), + DATA_ALREADY_EXISTED(50003, "数据已存在"), + + /* 接口错误:60001-69999 */ + INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"), + INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"), + INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"), + INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"), + INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"), + INTERFACE_EXCEED_LOAD(60006, "接口负载过高"), + + /* 权限错误:70001-79999 */ + PERMISSION_NO_ACCESS(70001, "无访问权限"); + + + private Integer code; + + private String message; + + ResultCode(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer code() { + return this.code; + } + + public String message() { + return this.message; + } + + public static String getMessage(String name) { + for (ResultCode item : ResultCode.values()) { + if (item.name().equals(name)) { + return item.message; + } + } + return name; + } + + public static Integer getCode(String name) { + for (ResultCode item : ResultCode.values()) { + if (item.name().equals(name)) { + return item.code; + } + } + return null; + } + + @Override + public String toString() { + return this.name(); + } + + //校验重复的code值 + public static void main(String[] args) { + ResultCode[] ApiResultCodes = ResultCode.values(); + List codeList = new ArrayList(); + for (ResultCode ApiResultCode : ApiResultCodes) { + if (codeList.contains(ApiResultCode.code)) { + System.out.println(ApiResultCode.code); + } else { + codeList.add(ApiResultCode.code()); + } + } + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/exception/APIException.java b/api-java-sdk/src/main/java/com/tokenview/api/exception/APIException.java new file mode 100644 index 0000000..6a08cfc --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/exception/APIException.java @@ -0,0 +1,31 @@ +package com.tokenview.api.exception; + +public class APIException extends RuntimeException { + private int code; + + public APIException(String message) { + super(message); + } + + public APIException(int code, String message) { + super(message); + this.code = code; + } + + + public APIException(Throwable cause) { + super(cause); + } + + public APIException(String message, Throwable cause) { + super(message, cause); + } + + @Override + public String getMessage() { + if (this.code != 0) { + return this.code + " : " + super.getMessage(); + } + return super.getMessage(); + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/result/HttpResult.java b/api-java-sdk/src/main/java/com/tokenview/api/result/HttpResult.java new file mode 100644 index 0000000..9b0eb5f --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/result/HttpResult.java @@ -0,0 +1,70 @@ +package com.tokenview.api.result; + +public class HttpResult { + private int code; + private String message; + private int errorCode; + private String errorMessage; + private String order_id; + private Boolean result; + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getOrder_id() { + return order_id; + } + + public void setOrder_id(String order_id) { + this.order_id = order_id; + } + + public Boolean getResult() { + return result; + } + + public void setResult(Boolean result) { + this.result = result; + } + + @Override + public String toString() { + return "\t\tResponse Body:{" + + "code=" + code + + ", message='" + message + '\'' + + ", errorCode=" + errorCode + + ", errorMessage='" + errorMessage + '\'' + + ", order_id='" + order_id + '\'' + + ", result=" + result + + '}'; + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/base/BaseService.java b/api-java-sdk/src/main/java/com/tokenview/api/service/base/BaseService.java new file mode 100644 index 0000000..020d0e7 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/base/BaseService.java @@ -0,0 +1,25 @@ +package com.tokenview.api.service.base; + +import com.alibaba.fastjson.JSONObject; + +public interface BaseService { + + //获取指定交易详情 + JSONObject getTransaction(String currency, String txid); + + //获取UTXO(类BTC)指定地址交易列表 + JSONObject getUTXOTransactionList(String currency, String address, String page, String size); + + //获取ACCOUNT(类ETH/TRX)指定地址交易列表 + JSONObject getACCOUNTTransactionList(String currency, String address, String page, String size); + + //获取UTXO(类BTC)指定地址余额 + JSONObject getUTXOAddressBalance(String currency, String address); + + //获取ACCOUNT(类ETH/TRX)指定地址余额 + JSONObject getACCOUNTAddressBalance(String currency, String address); + + //T获取ACCOUNT(ETH,ETC,NAS,NEO)指定地址信息 + JSONObject getACCOUNTAddressInfo(String currency, String address); + +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseAPI.java b/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseAPI.java new file mode 100644 index 0000000..96a4689 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseAPI.java @@ -0,0 +1,29 @@ +package com.tokenview.api.service.base.Impl; + +import com.alibaba.fastjson.JSONObject; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; + +public interface BaseAPI { + + @GET("/tx/{currency}/{txid}") + Call getTransaction(@Path("currency") String currency, @Path("txid") String txid); + + @GET("/address/{currency}/{address}/{page}/{size}") + Call getTransactionList(@Path("currency") String currency, @Path("address") String address, + @Path("page") String page, @Path("size") String size); + + @GET("/{currency}/address/normal/{address}/{page}/{size}") + Call getACCOUNTTransactionList(@Path("currency") String currency, @Path("address") String address, + @Path("page") String page, @Path("size") String size); + + @GET("/address/{currency}/{address}/1/1") + Call getAddressBalance(@Path("currency") String currency, @Path("address") String address); + + @GET("/addr/b/{currency}/{address}") + Call getACCOUNTAddressBalance(@Path("currency") String currency, @Path("address") String address); + + @GET("/{currency}/address/{address}") + Call getACCOUNTAddressInfo(@Path("currency") String currency, @Path("address") String address); +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseServiceImpl.java b/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseServiceImpl.java new file mode 100644 index 0000000..bf9d940 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/base/Impl/BaseServiceImpl.java @@ -0,0 +1,47 @@ +package com.tokenview.api.service.base.Impl; + +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.client.APIClient; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.service.base.BaseService; + +public class BaseServiceImpl implements BaseService { + + private APIClient client; + private BaseAPI api; + + public BaseServiceImpl(APIConfiguration config) { + this.client = new APIClient(config); + this.api = client.createService(BaseAPI.class); + } + + @Override + public JSONObject getTransaction(String currency, String txid){ + return this.client.executeSync(this.api.getTransaction(currency,txid)); + } + + @Override + public JSONObject getUTXOTransactionList(String currency,String address,String page,String size){ + return this.client.executeSync(this.api.getTransactionList(currency,address,page,size)); + } + + @Override + public JSONObject getACCOUNTTransactionList(String currency,String address,String page,String size){ + return this.client.executeSync(this.api.getACCOUNTTransactionList(currency,address,page,size)); + } + + @Override + public JSONObject getUTXOAddressBalance(String currency,String address){ + return this.client.executeSync(this.api.getAddressBalance(currency,address)); + } + + @Override + public JSONObject getACCOUNTAddressBalance(String currency,String address){ + return this.client.executeSync(this.api.getACCOUNTAddressBalance(currency,address)); + } + + @Override + public JSONObject getACCOUNTAddressInfo(String currency,String address){ + return this.client.executeSync(this.api.getACCOUNTAddressInfo(currency,address)); + } +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/WalletService.java b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/WalletService.java new file mode 100644 index 0000000..570bef4 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/WalletService.java @@ -0,0 +1,16 @@ +package com.tokenview.api.service.wallet; + +import com.alibaba.fastjson.JSONObject; + +public interface WalletService { + + //发送ETH/BTC交易到对应区块链 + JSONObject sendRawTransaction(String currency, JSONObject jo); + + //获取ACCOUNT(类ETH/TRX)指定地址信息 + JSONObject getACCOUNTAddressNonce(String currency, JSONObject jo); + + //获取TRX签名前内容 + JSONObject getTRXCreateTransaction(JSONObject jo); + +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletAPI.java b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletAPI.java new file mode 100644 index 0000000..74440df --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletAPI.java @@ -0,0 +1,20 @@ +package com.tokenview.api.service.wallet.impl; + +import com.alibaba.fastjson.JSONObject; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.POST; +import retrofit2.http.Path; + +public interface WalletAPI { + + @POST("/onchainwallet/{currency}") + Call sendRawTransaction(@Path("currency") String currency, @Body JSONObject jsonObject); + + @POST("/onchainwallet/{currency}") + Call getACCOUNTAddressNonce(@Path("currency") String currency, @Body JSONObject jsonObject); + + @POST("/onchainwallet/trx") + Call getTRXCreateTransaction(@Body JSONObject jsonObject); + +} diff --git a/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletServiceImpl.java b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletServiceImpl.java new file mode 100644 index 0000000..3572770 --- /dev/null +++ b/api-java-sdk/src/main/java/com/tokenview/api/service/wallet/impl/WalletServiceImpl.java @@ -0,0 +1,36 @@ +package com.tokenview.api.service.wallet.impl; + +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.client.APIClient; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.service.wallet.WalletService; + +public class WalletServiceImpl implements WalletService { + + private APIClient client; + private WalletAPI api; + + public WalletServiceImpl(APIConfiguration config) { + this.client = new APIClient(config); + this.api = client.createService(WalletAPI.class); + } + + @Override + public JSONObject sendRawTransaction(String currency,JSONObject jsonObject){ + return this.client.executeSync(this.api.sendRawTransaction(currency,jsonObject)); + } + + @Override + public JSONObject getACCOUNTAddressNonce(String currency,JSONObject jsonObject){ + return this.client.executeSync(this.api.getACCOUNTAddressNonce(currency,jsonObject)); + } + + @Override + public JSONObject getTRXCreateTransaction(JSONObject jsonObject){ + return this.client.executeSync(this.api.getTRXCreateTransaction(jsonObject)); + } + + + + +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseAPITest.java b/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseAPITest.java new file mode 100644 index 0000000..128579d --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseAPITest.java @@ -0,0 +1,99 @@ +package com.tokenview.api.test.base; + +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.service.base.BaseService; +import com.tokenview.api.service.base.Impl.BaseServiceImpl; +import org.junit.Before; +import org.junit.Test; + +/** + * TokenView API Test + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/20 11:13 + */ +public class BaseAPITest extends BaseConfig{ + + private BaseService baseService; + + @Before + public void before() { + config = config(); + baseService = new BaseServiceImpl(config); + } + + /** + * 查看交易详情,支持类BTC,类ETH,TRX等tokenview支持的所有币的交易查询 + * GET /tx/{currency}/{txid} + */ + @Test + public void testGetTransaction(){ + JSONObject result = baseService.getTransaction("btc","6680c65d3b1f4ab61b7096441a257de51f8bab48d0dca2f76080891ad9b0796a"); + toResultString("GetTransaction", result); + } + + /** + * 查看UTXO(类BTC)指定地址余额 + * GET /address/{currency}/{address}/{page}/{size} + */ + @Test + public void testGetUTXOAddressBalance(){ + JSONObject result = baseService.getUTXOAddressBalance("btc","1AqSbAsXh3zxE8Z61afpU65u4pxB9BBRcz"); + toResultString("GetUTXOAddressBalance", result); + } + + /** + * 查看UTXO(类BTC)指定地址交易列表 + * GET /address/{currency}/{address}/{page}/{size} + */ + @Test + public void testGetUTXOTransactionList(){ + JSONObject result = baseService.getUTXOTransactionList("btc","1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ","1","50"); + toResultString("GetUTXOTransactionList", result); + } + + /** + * 查看ACCOUNT(类ETH/TRX)指定地址余额 + * GET /address/{currency}/{address}/{page}/{size} + */ + @Test + public void testGetACCOUNTAddressBalance(){ + JSONObject result = baseService.getACCOUNTAddressBalance("eth","0xda9cacf6c13450bea275c33e83503fb705d27bbb"); + toResultString("GetACCOUNTAddressBalance", result); + } + + /** + * 查看获取ACCOUNT(类ETH/TRX)指定地址交易列表 + * GET /{currency}/address/normal/{address}/{page}/{size} + */ + @Test + public void testGetACCOUNTTransactionList(){ + JSONObject result = baseService.getACCOUNTTransactionList("eth","0x58b6a8a3302369daec383334672404ee733ab239","1","50"); + toResultString("GetACCOUNTTransactionList", result); + } + + /** + * 查看ACCOUNT(ETH,ETC,NAS,NEO)指定地址详细信息 + * GET /{currency}/address/{address} + */ + @Test + public void getACCOUNTAddressInfo(){ + JSONObject result = baseService.getACCOUNTAddressInfo("eth","0xda9cacf6c13450bea275c33e83503fb705d27bbb"); + toResultString("GetACCOUNTAddressInfo", result); + } + + /** + * 获取账户类型(类eth)地址信息(nonce),目前支持ETH,ETC,NAS,NEO,其他币暂时不支持 + * + */ + @Test + public void getACCOUNTAddressNonce(){ + String nonce = baseService.getACCOUNTAddressInfo("eth","0xda9cacf6c13450bea275c33e83503fb705d27bbb") + .getJSONObject("data") + .getString("nonce"); + toResultString("GetACCOUNTAddressNonce", nonce); + } + + +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseConfig.java b/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseConfig.java new file mode 100644 index 0000000..94b6529 --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/base/BaseConfig.java @@ -0,0 +1,29 @@ +package com.tokenview.api.test.base; + +import com.alibaba.fastjson.JSON; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.enums.I18nEnum; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class BaseConfig { + public APIConfiguration config; + + public APIConfiguration config() { + APIConfiguration config = new APIConfiguration(); + + config.setEndpoint("http://www.tokenview.com:8088/"); + config.setApiKey("ab2ac254-b5b3-4b13-bdea-e5d9a12319d7"); + + config.setPrint(true); + config.setI18n(I18nEnum.ENGLISH); + + return config; + } + + public void toResultString(String flag, Object object) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(flag).append(":\n").append(JSON.toJSONString(object)); + System.out.println(stringBuilder); + } +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesAPITest.java b/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesAPITest.java new file mode 100644 index 0000000..5157b2f --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesAPITest.java @@ -0,0 +1,11 @@ +package com.tokenview.api.test.services; + +/** + * TokenView API Test + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/20 11:44 + */ +public class ServicesAPITest extends ServicesConfig { +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesConfig.java b/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesConfig.java new file mode 100644 index 0000000..691cf8e --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/services/ServicesConfig.java @@ -0,0 +1,30 @@ +package com.tokenview.api.test.services; + +import com.alibaba.fastjson.JSON; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.enums.I18nEnum; + +public class ServicesConfig { + + + public APIConfiguration config; + + public APIConfiguration config() { + APIConfiguration config = new APIConfiguration(); + // apiKey,api注册成功后页面上有 + //config.setEndpoint("https://wallet.tokenview.com/"); + config.setEndpoint("http://www.tokenview.com:8088/"); + config.setApiKey("ab2ac254-b5b3-4b13-bdea-e5d9a12319d7"); + + config.setPrint(true); + config.setI18n(I18nEnum.ENGLISH); + + return config; + } + + public void toResultString(String flag, Object object) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(flag).append(":\n").append(JSON.toJSONString(object)); + System.out.println(stringBuilder); + } +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletAPITest.java b/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletAPITest.java new file mode 100644 index 0000000..006bf81 --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletAPITest.java @@ -0,0 +1,187 @@ +package com.tokenview.api.test.wallet; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.tokenview.api.service.wallet.WalletService; +import com.tokenview.api.service.wallet.impl.WalletServiceImpl; +import com.tokenview.sign.btc.BTCSign; +import com.tokenview.sign.eth.ETHSign; +import com.tokenview.utils.BTCWalletUtil; +import com.tokenview.utils.HttpUtil; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * TokenView API Test + * + * @author Moilk + * @version 1.0.0 + * @date 2021/1/20 16:09 + */ +public class WalletAPITest extends WalletConfig { + + private WalletService walletservice; + + @Before + public void before() { + config = config(); + walletservice = new WalletServiceImpl(config); + } + /**由助记词得到eckey+wif格式私钥+hex格式私钥,适用于类BTC币和类ETH币,详细见打印信息,请注意以下两点 + * 类BTC币请使用BTC_MAIN_PATH + * 类ETH币请使用ETH_MAIN_PATH + * 使用对的chainpath才能和目前市场主流钱包生成地址相同,否则不同!!!! + * @Param List + */ + @Test + public void getEckey(){ + List stringList = new ArrayList(); + stringList.add("gym"); + stringList.add("please"); + stringList.add("sauce"); + stringList.add("elephant"); + stringList.add("trap"); + stringList.add("bag"); + stringList.add("logic"); + stringList.add("okay"); + stringList.add("impulse"); + stringList.add("slogan"); + stringList.add("goose"); + stringList.add("birth"); + BTCWalletUtil.loadWalletByMnemonic("btc",stringList,"bc1q44elpv05mkdkp5qyd8fajcq2lrczpsats7zjgr","mywallet"); + BTCWalletUtil.loadWalletByMnemonic("eth",stringList,"bc1q44elpv05mkdkp5qyd8fajcq2lrczpsats7zjgr","mywallet"); + } + + /** + * 创建账户类型(类eth)地址 + * @Param privateKey + */ + @Test + public void createACCOUNTAddress(){ + toResultString("CreateACCOUNTAddress", new ETHSign().getAddress("b71a7616b42110d8345ddc6826ec42c2f1ce24d5f4d8efeb616168d5c1ef4a1f")); + } + + /** + * 创建UTXO(类BTC)地址 + * @Param privateKey + */ + @Test + public void createUTXOAddress(){ + toResultString("CreateUTXOAddress",new BTCSign().getAddress("ab4ff104eddcc43430e0afe9487f21e1033fdc4eca9448065593d49fbc5e6b53")); + } + + + /** + * 使用私钥对账户类型(类eth)地址发交易进行签名,nonce调用BaseAPI获取,gasPrice和gasLimit可使用下面固定值约 $2/笔 + * @Param privateKey + * @Param toAddress + * @Param nonce + * @Param gasPrice + * @Param gasLimit + * @Param amount + */ + @Test + public void SignEthTransaction(){ + toResultString("SignEthTransaction", new ETHSign().signETHTransaction( + "0xb71a7616b42110d8345ddc6826ec42c2f1ce24d5f4d8efeb616168d5c1ef4a1f", + "0x9Ae75431335d2e70f8DB0b35F6C179a43756f78e", + "1", + 78500000000L, + 21000L, + 100000000L)); + } + + /** + * 使用私钥对UTXO类型(类BTC)地址发交易进行签名,包含获取unspent并组装交易过程,暂不支持隔离见证bech32地址 + * @Param txid + * @Param inputAddress(可若干个) + * @Param toAddress(可若干个) + * @Param toAmount(可若干个) + * @Param changeAddress + * @Param privateKeyWif(privateKeyHex也可只要能生成Eckey,本次测试用wif格式) + */ + @Test + public void SignBTCTransaction(){ + //unspent所在txid和所在地址 + String txid = "6680c65d3b1f4ab61b7096441a257de51f8bab48d0dca2f76080891ad9b0796a"; + String inputAddress="1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ"; + String toAdddress = "3Kjn9rsoQPrHQXGCJjJuuqZe46JNAseN1t"; + long toAmount = 10000; + String changeAddress="1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ"; + String privateKeyWIF = "L2xigpefbyesCnzR5hJDFxKPD4A2qSuNHiz4utWnqEovhownw8U2"; + + List addrList = new ArrayList<>(); + addrList.add(inputAddress); + //可自行设置多个输出地址和金额,找零地址和金额 + JSONArray unspents =new HttpUtil().getUnspent(txid,addrList); + long inputAmount =unspents + .stream() + .mapToLong(o->JSONObject.parseObject(o.toString()).getLong("value")) + .sum(); + + JSONArray outputs = new JSONArray(); + JSONObject outputTo = new JSONObject(); + outputTo.put("address",toAdddress); + outputTo.put("amount",toAmount); + outputs.add(outputTo); + long outputToAmount = outputs + .stream() + .mapToLong(o -> JSONObject.parseObject(o.toString()).getLong("amount")) + .sum(); + long outputChangeAmount = inputAmount - outputToAmount; + //input金额不足输出和找零返回不签名 + if (outputChangeAmount<=0){ + return; + } + JSONObject outputChange = new JSONObject(); + outputChange.put("address",changeAddress); + outputChange.put("amount",outputChangeAmount); + + outputs.add(outputChange); + + //找零金额小于等于手续费返回不签名 + long fee = (unspents.size() + outputs.size() * 2) * 148 - 10; + if (outputChangeAmount <= fee) { + return; + } + toResultString("SignBTCTransaction",new BTCSign().signBTCTransaction(privateKeyWIF,unspents,outputs)); + } + + + /** + * 广播类btc/eth/etc交易上链,对应不同method,注意替换 + * POST /onchainwallet/{currency} + * @Param method + * @Param signature + * @Param currency + */ + @Test + public void testSendETHRawTransaction() { + + JSONObject jo =new JSONObject(); + jo.put("jsonrpc","2.0"); + jo.put("id","viewtoken"); + jo.put("method","eth_sendRawTransaction"); + jo.put("params", Arrays.asList("0xf86801851246f6f100825208949ae75431335d2e70f8db0b35f6c179a43756f78e8405f5e100801ca058b2130954d3b84918db5c0fc309dcc942b8656d88964a5f981a4a954bbb1221a0788b6b75afaea28d2e9178a6cea3d432983c10db1c778a1dcb6af009f5457701")); + JSONObject onchainwallet = walletservice.sendRawTransaction("eth",jo); + toResultString("SendRawTransaction", onchainwallet); + } + @Test + public void testSendBTCRawTransaction(){ + JSONObject jo =new JSONObject(); + jo.put("jsonrpc","2.0"); + jo.put("id","viewtoken"); + jo.put("method","sendrawtransaction"); + jo.put("params", Arrays.asList("01000000016a79b0d91a898060f7a2dcd048ab8b1fe57d251a4496701bb64a1f3b5dc68066010000006b483045022100f5c22019fdd167dfe16988cabbe31a4eeda609abfd8369612813cd510cadca5102201270c8dca6a6b7826162d7f5b00c7a479ad32505a55e958301473bdcab72079f8121033efaa795ed22b1531127a3fd5b6e72031f6a7f4370fb67367768054ef011def0ffffffff02102700000000000017a914c64907144796b4177d6b5809c388c437385db4bb879ab00000000000001976a914d2ed73cd81bb2d516411c47a8ddb02ae5f2e7d0188ac00000000")); + JSONObject onchainwallet = walletservice.sendRawTransaction("btc",jo); + toResultString("SendRawTransaction", onchainwallet); + } + + + + +} diff --git a/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletConfig.java b/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletConfig.java new file mode 100644 index 0000000..469ba8a --- /dev/null +++ b/api-java-sdk/src/test/java/com/tokenview/api/test/wallet/WalletConfig.java @@ -0,0 +1,29 @@ +package com.tokenview.api.test.wallet; + +import com.alibaba.fastjson.JSON; +import com.tokenview.api.config.APIConfiguration; +import com.tokenview.api.enums.I18nEnum; + +public class WalletConfig { + + public APIConfiguration config; + + public APIConfiguration config() { + APIConfiguration config = new APIConfiguration(); + // apiKey,api注册成功后页面上有 + config.setEndpoint("https://wallet.tokenview.com/"); + config.setApiKey("ab2ac254-b5b3-4b13-bdea-e5d9a12319d7"); + + config.setPrint(true); + config.setI18n(I18nEnum.ENGLISH); + + return config; + } + + public void toResultString(String flag, Object object) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(flag).append(":\n").append(JSON.toJSONString(object)); + System.out.println(stringBuilder); + } + +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1704cf6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + com.tokenview + api-demo + pom + 1.0.0 + + sign-java-sdk + api-java-sdk + + + + diff --git a/sign-java-sdk/pom.xml b/sign-java-sdk/pom.xml new file mode 100644 index 0000000..a892428 --- /dev/null +++ b/sign-java-sdk/pom.xml @@ -0,0 +1,210 @@ + + + + api-demo + com.tokenview + 1.0.0 + + 4.0.0 + + com.tokenview + sign-java-sdk + 1.0.0 + + + + cash.bitcoinj + bitcoinj-tools + 0.14.5.2 + + + + + + + + org.web3j + core + 3.0.1 + + + jnr-ffi + com.github.jnr + + + okio + com.squareup.okio + + + bcprov-jdk15on + org.bouncycastle + + + slf4j-api + org.slf4j + + + okhttp + com.squareup.okhttp3 + + + jackson-databind + com.fasterxml.jackson.core + + + + + org.web3j + crypto + 4.2.0 + + + org.slf4j + slf4j-log4j12 + 1.8.0-beta2 + + + org.springframework + spring-context + 5.1.5.RELEASE + + + com.squareup.retrofit2 + converter-scalars + 2.3.0 + + + com.squareup.retrofit2 + converter-gson + 2.3.0 + + + com.squareup.retrofit2 + adapter-rxjava + 2.3.0 + + + org.apache.commons + commons-lang3 + 3.7 + + + ch.qos.logback + logback-classic + 1.2.3 + + + com.google.guava + guava + 27.1-jre + + + commons-codec + commons-codec + 1.12 + + + org.apache.commons + commons-compress + 1.18 + + + org.projectlombok + lombok + 1.18.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.5 + + + commons-beanutils + commons-beanutils + 1.9.3 + + + commons-collections + commons-collections + 3.2.1 + + + commons-logging + commons-logging + 1.1.1 + + + net.sf.ezmorph + ezmorph + 1.0.6 + + + com.alibaba + fastjson + 1.2.12 + + + net.sf.json-lib + json-lib + 2.2.3 + jdk15 + + + org.springframework + spring-web + 5.0.9.RELEASE + test + + + jstl + jstl + 1.2 + test + + + junit + junit + 4.12 + test + + + com.madgag.spongycastle + prov + 1.58.0.0 + + + + + + + + org.apache.httpcomponents + httpclient + 4.5.8 + + + org.bitcoincashj + bitcoincashj-core + 1.1.0-SNAPSHOT + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + \ No newline at end of file diff --git a/sign-java-sdk/sign-java-sdk.iml b/sign-java-sdk/sign-java-sdk.iml new file mode 100644 index 0000000..e5fc851 --- /dev/null +++ b/sign-java-sdk/sign-java-sdk.iml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sign-java-sdk/src/main/java/com/tokenview/bean/WalletBean.java b/sign-java-sdk/src/main/java/com/tokenview/bean/WalletBean.java new file mode 100644 index 0000000..50da4cc --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/bean/WalletBean.java @@ -0,0 +1,67 @@ +package com.tokenview.bean; + +public class WalletBean { + private String coin_type; + private String mnemonic; + private String address; + private String keystore; + private String privateKey; + private String name; + private int insert_type; + + public String getCoin_type() { + return coin_type; + } + + public void setCoin_type(String coin_type) { + this.coin_type = coin_type; + } + + public String getMnemonic() { + return mnemonic; + } + + public void setMnemonic(String mnemonic) { + this.mnemonic = mnemonic; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getKeystore() { + return keystore; + } + + public void setKeystore(String keystore) { + this.keystore = keystore; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getInsert_type() { + return insert_type; + } + + public void setInsert_type(int insert_type) { + this.insert_type = insert_type; + } +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/enums/Version.java b/sign-java-sdk/src/main/java/com/tokenview/enums/Version.java new file mode 100644 index 0000000..81b1b72 --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/enums/Version.java @@ -0,0 +1,15 @@ +package com.tokenview.enums; + +public enum Version { + BTC(1); + + private Integer version; + + Version(Integer version) { + this.version = version; + } + + public Integer getVersion() { + return version; + } +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/sign/btc/BTCSign.java b/sign-java-sdk/src/main/java/com/tokenview/sign/btc/BTCSign.java new file mode 100644 index 0000000..7dd15f6 --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/sign/btc/BTCSign.java @@ -0,0 +1,59 @@ +package com.tokenview.sign.btc; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.tokenview.enums.Version; +import org.bitcoinj.core.*; +import org.bitcoinj.params.MainNetParams; +import org.bitcoinj.script.Script; +import org.spongycastle.util.encoders.Hex; + +public class BTCSign { + public String signBTCTransaction(String privateKey,JSONArray unspents,JSONArray outputs) { + String hexString = ""; + try { + NetworkParameters params = MainNetParams.get(); + DumpedPrivateKey priKey = DumpedPrivateKey.fromBase58(params,privateKey); + ECKey ecKey = priKey.getKey(); + System.out.println(ecKey.getPrivateKeyAsHex()); + System.out.println(ecKey.getPublicKeyAsHex()); + System.out.println(new String(ecKey.getPubKeyHash())); + //构建交易体 + Transaction transaction = new Transaction(params); + //交易的版本 默认的BTC,LTC ,DOGE,DASH 版本号是1, BCH BSV 版本号是:2; + transaction.setVersion(Version.BTC.getVersion()); + long fee = (unspents.size() + outputs.size() * 2) * 148 - 10; + //添加转出到的地址列表和数量 + for(int i = 0;i inputParameters = new ArrayList<>(); + inputParameters.add(new Address(toAddress)); + inputParameters.add(new Uint256(0L));//转出数量 + List> outputParameters = new ArrayList<>(); + //调用的合约的方法 transfer,参数,输出 等等 + Function function = new Function("transfer", inputParameters, outputParameters); + //将合约数据转换成16进制字符串 + String dataStr = FunctionEncoder.encode(function); + RawTransaction tx = RawTransaction.createTransaction( + BigInteger.valueOf(Long.parseLong(nonce)), + BigInteger.valueOf(gasPrice),//gasPrice + BigInteger.valueOf(gasLimit),//gasLimit + toAddress, + BigInteger.valueOf(amount),//转账的"数量" + ""); + //创建钥匙对 + ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.toBigInt(privateKey)); + Credentials credentials = Credentials.create(ecKeyPair); + System.out.println(credentials.getAddress()); + //签名交易 + byte[] signed = TransactionEncoder.signMessage(tx, credentials); + //将签名的交易转换成16进制字符串,并用于广播交易 + signedStr = Numeric.toHexString(signed); + } catch (Exception e) { + + } + return signedStr; + } + + public String getAddress(String privateKey){ + ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.toBigInt(privateKey)); + Credentials credentials = Credentials.create(ecKeyPair); + return credentials.getAddress(); + } +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/utils/BTCWalletUtil.java b/sign-java-sdk/src/main/java/com/tokenview/utils/BTCWalletUtil.java new file mode 100644 index 0000000..4f4bade --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/utils/BTCWalletUtil.java @@ -0,0 +1,304 @@ +package com.tokenview.utils; + + +import com.tokenview.bean.WalletBean; +import org.apache.commons.lang3.StringUtils; +import org.bitcoinj.core.*; +import org.bitcoinj.crypto.ChildNumber; +import org.bitcoinj.crypto.DeterministicKey; +import org.bitcoinj.crypto.HDKeyDerivation; +import org.bitcoinj.params.MainNetParams; +import org.bitcoinj.wallet.DeterministicSeed; +import org.bouncycastle.util.encoders.Hex; +import org.web3j.crypto.ECKeyPair; +import org.web3j.utils.Numeric; + +import java.util.List; + + +public class BTCWalletUtil { + + // public static boolean BTC_TEST_NET = SharedPreferencesUtil.getInstance().getBoolean(NumberConstant.IS_TEST_NET,false); + public static String BTC_TEST_PATH = "m/44'/1'/0'/0/0"; + public static String BTC_MAIN_PATH = "m/44'/0'/0'/0/0"; + public static String BTC_SEGWIT_MAIN_PATH = "m/49'/0'/0'/0/0"; + public static String BTC_SEGWIT_TEST_PATH = "m/49'/1'/0'/0/0"; + public static String ETH_MAIN_PATH="m/44'/60'/0'/0/0"; + + /** + * 通过助记词生成私钥 + */ + public static WalletBean loadWalletByDeterministicSeed(DeterministicSeed ds, String pwd, String walletName) { + String path; + NetworkParameters params; + path = BTC_MAIN_PATH; + params = MainNetParams.get(); + ECKeyPair keyPair = getEcKeyPairByDeterministicSeed(path, ds); +// loadWalletBIP49ByDeterministicSeed(ds); + if (keyPair == null) return null; + // 获取Base64编码的64位的私钥 +// String privateKey = Numeric.toHexStringNoPrefixZeroPadded(keyPair.getPrivateKey(), Keys.PRIVATE_KEY_LENGTH_IN_HEX); +// //第二种方式生成 +// ECKeyPair ecKeyPair = generateEcKeyPair(ds); + ECKey ecKey = ECKey.fromPrivate(keyPair.getPrivateKey()); + //获取Base58编码压缩后的私钥 + String privateKeyAsWiF = ecKey.getPrivateKeyAsWiF(params); + String address = ecKey.toAddress(params).toString(); + WalletBean bean = new WalletBean(); + bean.setCoin_type("BTC"); + bean.setMnemonic(getMnemonic(ds)); + bean.setAddress(address); + bean.setKeystore(""); + bean.setPrivateKey(privateKeyAsWiF); + bean.setName(walletName); + return bean; + } + + + /** + * 通过助记词生成 BIP49 协议的隔离见证地址 + */ + public static WalletBean loadWalletBIP49ByDeterministicSeed(List mnemonic) { + DeterministicSeed ds = getDeterministicSeed(mnemonic); + String path=BTC_SEGWIT_MAIN_PATH; + NetworkParameters params= MainNetParams.get(); + String[] pathArray = path.split("/"); + byte[] seedBytes = ds.getSeedBytes(); + DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes); + for (int i = 1; i < pathArray.length; i++) { + ChildNumber childNumber; + if (pathArray[i].endsWith("'")) { + int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1)); + childNumber = new ChildNumber(number, true); + } else { + int number = Integer.parseInt(pathArray[i]); + childNumber = new ChildNumber(number, false); + } + dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber); + } + //获取P2SH地址的过程 + String publicKey=Numeric.toHexStringNoPrefix(dkKey.getPubKeyHash()); + String redeemScript = String.format("0x0014%s", publicKey); + byte[] bytes = Numeric.hexStringToByteArray(redeemScript); + byte[] bytes1 = Utils.sha256hash160(bytes); + String p2shAddress = Address.fromP2SHHash(params, bytes1).toBase58(); + System.out.println("p2sh address == "+p2shAddress); + WalletBean bean=new WalletBean(); + bean.setCoin_type("BTC"); + bean.setName("BTC"); + bean.setAddress(p2shAddress); + bean.setMnemonic(getMnemonic(ds)); + bean.setInsert_type(1); + bean.setPrivateKey(dkKey.getPrivateKeyAsWiF(params)); + return bean; + } + + + + /** + * 通过助记词生成hex私钥,WIF私钥,也可生成地址 + */ + public static WalletBean loadWalletByDeterministicSeedTest(DeterministicSeed ds, String pwd, String walletName) { + String path; + NetworkParameters params; + path = BTC_MAIN_PATH; + params = MainNetParams.get(); + //params = TestNet3Params.get(); + ECKeyPair keyPair = getEcKeyPairByDeterministicSeed(path, ds); + if (keyPair == null) return null; + // 获取Base64编码的64位的私钥 +// String privateKey = Numeric.toHexStringNoPrefixZeroPadded(keyPair.getPrivateKey(), Keys.PRIVATE_KEY_LENGTH_IN_HEX); + // 第二种方式生成 + // ECKeyPair ecKeyPair = generateEcKeyPair(ds); + ECKey ecKey = ECKey.fromPrivate(keyPair.getPrivateKey()); + String hexPrivateKey = ecKey.getPrivateKeyAsHex(); + ECKey ecKey1 = ECKey.fromPrivate(Hex.decode(hexPrivateKey)); + Address address1 = ecKey1.toAddress(params); + System.out.println("bitcoin address == "+address1); + //获取Base58编码压缩后的私钥 + System.out.println("privateKeyHex == "+ecKey.getPrivateKeyAsHex()); + //ecKey. + String privateKeyAsWiF = ecKey.getPrivateKeyAsWiF(params); + System.out.println("privateKeyWif =="+privateKeyAsWiF); + String address = ecKey.toAddress(params).toString(); + System.out.println("bitcoin address == "+address); + WalletBean bean = new WalletBean(); + bean.setCoin_type("BTC"); + bean.setMnemonic(getMnemonic(ds)); + bean.setAddress(address); + bean.setKeystore(""); + bean.setPrivateKey(privateKeyAsWiF); + bean.setName(walletName); + return bean; + } + + /** + * 通过私钥导入BTC钱包 (base58) + * */ + public static WalletBean loadWalletByPrivateKey(String privateKey) { + NetworkParameters params; + params = MainNetParams.get(); + try { + DumpedPrivateKey priKey = DumpedPrivateKey.fromBase58(params, privateKey); + ECKey ecKey = priKey.getKey(); + String address = ecKey.toAddress(params).toString(); + WalletBean bean = new WalletBean(); + bean.setCoin_type("BTC"); + bean.setMnemonic(" "); + bean.setAddress(address); + bean.setKeystore(""); + bean.setPrivateKey(privateKey); + bean.setName(" "); + return bean; + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + + /** + * 通过助记词生成私钥 + */ + public static WalletBean loadWalletByMnemonic(String currency,List list, String pwd, String walletName) { + String path; + NetworkParameters params; + path = ""; + if (currency.equalsIgnoreCase("btc")) { + path = BTC_MAIN_PATH; + } + if (currency.equalsIgnoreCase("eth")) { + path = ETH_MAIN_PATH; + } + params = MainNetParams.get(); + DeterministicSeed ds = getDeterministicSeed(list); + ECKeyPair keyPair = getEcKeyPairByDeterministicSeed(path, ds); +// //获取16编码的64位的私钥 +// String privateKey = Numeric.toHexStringNoPrefixZeroPadded(keyPair.getPrivateKey(), Keys.PRIVATE_KEY_LENGTH_IN_HEX); +// ECKeyPair ecKeyPair = generateEcKeyPair(ds); + ECKey ecKey = ECKey.fromPrivate(keyPair.getPrivateKey()); + //获取Base58编码压缩后的私钥 + String privateKeyAsWiF = ecKey.getPrivateKeyAsWiF(params); + String address = ecKey.toAddress(params).toString(); + System.out.println("privateKey ===="+ecKey.getPrivateKeyAsHex()); + System.out.println("address ====="+address); + WalletBean bean = new WalletBean(); + bean.setCoin_type("BTC"); + bean.setMnemonic(getMnemonic(ds)); + bean.setAddress(address); + bean.setKeystore(""); + bean.setPrivateKey(privateKeyAsWiF); + bean.setName(walletName); + return bean; + } + + + /** + * 通过路径和种子获取私钥对 + */ + public static ECKeyPair getEcKeyPairByDeterministicSeed(String path, DeterministicSeed ds) { + String[] pathArray = path.split("/"); + byte[] seedBytes = ds.getSeedBytes(); + if (seedBytes == null) + return null; + DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes); + for (int i = 1; i < pathArray.length; i++) { + ChildNumber childNumber; + if (pathArray[i].endsWith("'")) { + int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1)); + childNumber = new ChildNumber(number, true); + } else { + int number = Integer.parseInt(pathArray[i]); + childNumber = new ChildNumber(number, false); + } + dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber); + } + ECKeyPair keyPair = ECKeyPair.create(dkKey.getPrivKeyBytes()); + return keyPair; + } + + + + /** + * 通过路径和种子获取私钥对 + */ + public static DeterministicKey getPrivateBySeed(String path, DeterministicSeed ds) { + String[] pathArray = path.split("/"); + byte[] seedBytes = ds.getSeedBytes(); + if (seedBytes == null) + return null; + DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes); + for (int i = 1; i < pathArray.length; i++) { + ChildNumber childNumber; + if (pathArray[i].endsWith("'")) { + int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1)); + childNumber = new ChildNumber(number, true); + } else { + int number = Integer.parseInt(pathArray[i]); + childNumber = new ChildNumber(number, false); + } + dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber); + } +// ECKeyPair keyPair = ECKeyPair.create(dkKey.getPrivKeyBytes()); + return dkKey; + } + + + + /** + * 通过助记词生成种子 + * */ + public static DeterministicSeed getDeterministicSeed(List list) { + try { + long creationTimeSeconds = System.currentTimeMillis() / 1000; + return new DeterministicSeed(list, null, "", creationTimeSeconds); + }catch (Exception e){} + return null; + } + + /** + * 通过助记词生成Hex私钥 + * */ + public static String getPrivateKeyHex(List list) { + try { + long creationTimeSeconds = System.currentTimeMillis() / 1000; + DeterministicSeed seed = new DeterministicSeed(list, null, "", creationTimeSeconds); + ECKeyPair keyPair = getEcKeyPairByDeterministicSeed("m/44'/1'/0'/0/0", seed); + if (keyPair == null) return null; + ECKey ecKey = ECKey.fromPrivate(keyPair.getPrivateKey()); + String hexPrivateKey = ecKey.getPrivateKeyAsHex(); + return hexPrivateKey; + }catch (Exception e){} + return null; + } + + /** + * 通过种子生成助记词字符串 + */ + public static String getMnemonic(DeterministicSeed ds) { + StringBuilder sb = new StringBuilder(); + List mnemonicList = ds.getMnemonicCode(); + for (int i = 0; mnemonicList != null && i < mnemonicList.size(); i++) { + sb.append(mnemonicList.get(i) + " "); + } + return sb.toString().trim(); + } + + /**判断地址的有效性*/ + public static boolean isBTCValidAddress(String input) { + if (StringUtils.isEmpty(input))return false; + try { + NetworkParameters networkParameters = null; + networkParameters = MainNetParams.get(); + Address address = Address.fromBase58(networkParameters, input); + if (address != null) + return true; + else + return false; + } catch (Exception e) { + return false; + } + } + + +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/utils/HexUtil.java b/sign-java-sdk/src/main/java/com/tokenview/utils/HexUtil.java new file mode 100644 index 0000000..21bfb2d --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/utils/HexUtil.java @@ -0,0 +1,80 @@ +package com.tokenview.utils; + +/** + * @author zhaoda + * @date 2020/6/1. + * GitHub: + * email: + * description: + */ +public class HexUtil { + private static final char[] digits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + public static final byte[] emptybytes = new byte[0]; + + public HexUtil() { + } + + public static String byte2HexStr(byte b) { + char[] buf = new char[]{'\u0000', digits[b & 15]}; + b = (byte)(b >>> 4); + buf[0] = digits[b & 15]; + return new String(buf); + } + + public static String bytes2HexStr(byte[] bytes) { + if (bytes != null && bytes.length != 0) { + char[] buf = new char[2 * bytes.length]; + + for(int i = 0; i < bytes.length; ++i) { + byte b = bytes[i]; + buf[2 * i + 1] = digits[b & 15]; + b = (byte)(b >>> 4); + buf[2 * i + 0] = digits[b & 15]; + } + + return new String(buf); + } else { + return null; + } + } + + public static byte hexStr2Byte(String str) { + return str != null && str.length() == 1 ? char2Byte(str.charAt(0)) : 0; + } + + public static byte char2Byte(char ch) { + if (ch >= '0' && ch <= '9') { + return (byte)(ch - 48); + } else if (ch >= 'a' && ch <= 'f') { + return (byte)(ch - 97 + 10); + } else { + return ch >= 'A' && ch <= 'F' ? (byte)(ch - 65 + 10) : 0; + } + } + + public static byte[] hexStr2Bytes(String str) { + if (str != null && !str.equals("")) { + byte[] bytes = new byte[str.length() / 2]; + + for(int i = 0; i < bytes.length; ++i) { + char high = str.charAt(i * 2); + char low = str.charAt(i * 2 + 1); + bytes[i] = (byte)(char2Byte(high) * 16 + char2Byte(low)); + } + + return bytes; + } else { + return emptybytes; + } + } + + public static void main(String[] args) { + try { + byte[] bytes = "Hello WebSocket World?".getBytes("gbk"); + System.out.println(bytes2HexStr(bytes)); + } catch (Exception var3) { + var3.printStackTrace(); + } + + } +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/utils/HttpClientUtils.java b/sign-java-sdk/src/main/java/com/tokenview/utils/HttpClientUtils.java new file mode 100644 index 0000000..0d6f2ff --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/utils/HttpClientUtils.java @@ -0,0 +1,370 @@ +package com.tokenview.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.*; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.*; + +@Deprecated +@Slf4j +public final class HttpClientUtils { + + private static final Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); + + public static String get(final String url, final Map params, final Map headerMap) { + HttpGet httpGet = null; + try { + URIBuilder ub = new URIBuilder(url); + List pairs = Lists.newArrayList(); + + if (params != null && !params.isEmpty()) { + for (Map.Entry entry : params.entrySet()) { + pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString())); + } + + ub.setParameters(pairs); + } + httpGet = new HttpGet(ub.build()); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return execute(httpGet, headerMap); + } + + public static String get(final String url, final Map headerMap) { + return get(url, null, headerMap); + } + + public static JSONObject getReturnObject(final String url, final Map headerMap) { + String str = get(url, null, headerMap); + JSONObject json = null; + try { + json = JSONObject.parseObject(str); + } catch (Exception e) { + log.info("this resquest return not json object" + str); + json = new JSONObject(); + } + + return json; + } + + public static JSONArray getReturnArray(final String url, final Map headerMap) { + String str = get(url, null, headerMap); + JSONArray json = null; + try { + json = JSONArray.parseArray(str); + } catch (Exception e) { + log.info("this resquest return not json array" + str); + json = new JSONArray(); + } + + return json; + } + + public static String post(final String url, final Map params, + final Map headerMap) { + return post(url, params, headerMap, "utf-8"); + } + + public static String post(final String url, final Map params) { + return post(url, params, null, "utf-8"); + } + + public static String post(final String url, final Map params, final Map headerMap, + final String charset) { + final HttpPost request = new HttpPost(url); + if (params != null && params.size() > 0) { + List paramList = null; + final Set> entrySet = params.entrySet(); + paramList = new ArrayList<>(); + for (final Iterator> it = entrySet.iterator(); it.hasNext(); ) { + final Map.Entry entry = it.next(); + final String key = entry.getKey(); + final Object value = entry.getValue(); + if (key != null && value != null) { + final NameValuePair nvp = new BasicNameValuePair(key, value.toString()); + paramList.add(nvp); + } + } + try { + if (StringUtils.isEmpty(charset)) { + request.setEntity(new UrlEncodedFormEntity(paramList)); + } else { + request.setEntity(new UrlEncodedFormEntity(paramList, Charset.forName(charset))); + } + } catch (final Exception e) { + logger.error("HttpClientUtils post", e); + return null; + } + } + return execute(request, headerMap); + } + + public static String post(final String url, final HttpEntity entity, final Map headerMap) { + final HttpPost request = new HttpPost(url); + request.setEntity(entity); + return execute(request, headerMap); + } + + public static String execute(final HttpRequestBase request, final Map headerMap) { + return execute(null, request, headerMap); + } + + public static String execute(CloseableHttpClient httpclient, final HttpRequestBase request, + final Map headerMap) { + return execute(null, request, headerMap, 30000, 30000); + } + + public static String execute(final HttpRequestBase request, + final Map headerMap, int socketTimeout, int connectTimeout) { + return execute(null, request, headerMap, socketTimeout, connectTimeout); + } + + private static String execute(CloseableHttpClient httpclient, final HttpRequestBase request, + final Map headerMap, int socketTimeout, int connectTimeout) { + final StringBuffer log = new StringBuffer( + "HttpClientUtils execute method:" + request.getMethod() + " url:" + request.getURI()); + + boolean isClose = false; + if (httpclient == null) { + httpclient = HttpClients.createDefault(); + isClose = true; + } + InputStream resStream = null; + InputStreamReader inputStreamReader = null; + BufferedReader br = null; + String result = ""; + + if (headerMap != null && headerMap.size() > 0) { + final Iterator iterator = headerMap.keySet().iterator(); + while (iterator.hasNext()) { + final String key = iterator.next(); + request.setHeader(key, headerMap.get(key)); + } + } + + try { + final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout( + connectTimeout) + .build(); + request.setConfig(requestConfig); + final CloseableHttpResponse response = httpclient.execute(request); + + try { + + final HttpEntity entity = response.getEntity(); + + if (entity != null) { + resStream = entity.getContent(); + try { + inputStreamReader = new InputStreamReader(resStream); + br = new BufferedReader(inputStreamReader); + final StringBuffer resBuffer = new StringBuffer(); + String resTemp = ""; + while ((resTemp = br.readLine()) != null) { + resBuffer.append(resTemp); + } + result = resBuffer.toString(); + } finally { + if (br != null) { + br.close(); + } + if (inputStreamReader != null) { + inputStreamReader.close(); + } + if (resStream != null) { + resStream.close(); + } + } + } + } finally { + response.close(); + } + } catch (final Exception e) { + request.abort(); + logger.error(log.toString(), e); + } finally { + request.abort(); + try { + if (isClose) { + httpclient.close(); + } + } catch (final IOException e) { + logger.error(log.toString(), e); + } + } + return result; + } + + public static JSONObject post(String url, JSONObject params, Map header) { + try { + + //创建post方式请求对象 + HttpPost httpPost = new HttpPost(url); + if (header != null) { + for (Map.Entry entry : header.entrySet()) { + httpPost.setHeader(entry.getKey(), entry.getValue()); + } + } else { + //设置header信息 + httpPost.setHeader("Content-type", "application/json;charset=utf-8"); + httpPost.setHeader("Accept", "application/json;charset=utf-8"); + } + + // + StringEntity requestEntity = new StringEntity( + params.toJSONString(), + ContentType.APPLICATION_JSON); + httpPost.setEntity(requestEntity); + + log.debug("请求地址:" + url); + log.debug("请求参数:" + params.toJSONString()); + + return JSONObject.parseObject(send(httpPost, "UTF-8")); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public static String httpsGet(String url, Map header) { + try { + //创建post方式请求对象 + HttpGet httpGet = new HttpGet(url); + + if (header != null) { + for (Map.Entry entry : header.entrySet()) { + httpGet.setHeader(entry.getKey(), entry.getValue()); + } + } + + //设置header信息 + httpGet.setHeader("Content-type", "application/json;charset=utf-8"); + httpGet.setHeader("Accept", "application/json;charset=utf-8"); + + log.debug("请求地址:" + url); + + return send(httpGet, "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException { + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + + // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法 + X509TrustManager trustManager = new X509TrustManager() { + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + + sc.init(null, new TrustManager[] {trustManager}, null); + return sc; + } + + public static String send(HttpRequestBase requestBase, String encoding) + throws KeyManagementException, NoSuchAlgorithmException, IOException { + String body = ""; + //采用绕过验证的方式处理https请求 + SSLContext sslcontext = createIgnoreVerifySSL(); + + // 设置协议http和https对应的处理socket链接工厂的对象 + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", new SSLConnectionSocketFactory(sslcontext)) + .build(); + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + HttpClients.custom().setConnectionManager(connManager); + + //创建自定义的httpclient对象 + CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).setSSLHostnameVerifier( + new TrustAnyHostnameVerifier()).build(); + + //CloseableHttpClient client = HttpClients.createDefault(); + + //执行请求操作,并拿到结果(同步阻塞) + CloseableHttpResponse response = client.execute(requestBase); + //获取结果实体 + HttpEntity entity = response.getEntity(); + if (entity != null) { + //按指定编码转换结果实体为String类型 + body = EntityUtils.toString(entity, encoding); + } + EntityUtils.consume(entity); + //释放链接 + response.close(); + client.close(); + connManager.close(); + return body; + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } + + public static String put(final String url, final HttpEntity entity, final Map headerMap) { + final HttpPut request = new HttpPut(url); + request.setEntity(entity); + return execute(request, headerMap); + } + + public static String put(final String url, final Map headerMap) { + final HttpPut request = new HttpPut(url); + return execute(request, headerMap); + } + +} diff --git a/sign-java-sdk/src/main/java/com/tokenview/utils/HttpUtil.java b/sign-java-sdk/src/main/java/com/tokenview/utils/HttpUtil.java new file mode 100644 index 0000000..f4a96f9 --- /dev/null +++ b/sign-java-sdk/src/main/java/com/tokenview/utils/HttpUtil.java @@ -0,0 +1,55 @@ +package com.tokenview.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class HttpUtil { + + private String get(String url) { + return HttpClientUtils.get(url, new HashMap<>()); + } + + public JSONObject getTransactionDetail(String tx){ + StringBuffer url = new StringBuffer("https://chain.api.btc.com/v3/tx/"); + url.append(tx); + url.append("?verbose=3"); + String result = null; + try { + result = get(url.toString()); + } catch (Exception e) { + log.info(e.getMessage(), e); + } + return JSONObject.parseObject(result); + } + + public JSONArray getUnspent(String txid,List addrList){ + JSONObject result =new HttpUtil().getTransactionDetail(txid).getJSONObject("data"); + long height =result.getLong("block_height"); + JSONArray outputsArray = result.getJSONArray("outputs"); + JSONArray unspents = new JSONArray(); + int index =0; + for (Object o : outputsArray){ + Object io = JSONObject.parseObject(o.toString()).get("addresses"); + String address = String.valueOf(io).replace("[\"","").replace("\"]",""); + if (addrList.contains(address)){ + JSONObject txJson = JSONObject.parseObject(o.toString()); + txJson.put("txid",txid); + txJson.put("tx_output_n",index); + txJson.put("block_height",height); + unspents.add(txJson); + } + index++; + } + log.info(JSON.toJSONString(unspents)); + return unspents; + } +} diff --git a/sign-java-sdk/src/test/java/com/tokenview/sign/test/BTCSignTest.java b/sign-java-sdk/src/test/java/com/tokenview/sign/test/BTCSignTest.java new file mode 100644 index 0000000..0ece12d --- /dev/null +++ b/sign-java-sdk/src/test/java/com/tokenview/sign/test/BTCSignTest.java @@ -0,0 +1,76 @@ +package com.tokenview.sign.test; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.tokenview.sign.btc.BTCSign; +import com.tokenview.utils.HttpUtil; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class BTCSignTest { + + @Test + public void testGetUnspent(){ + String txid = "13c6235ee5e6403606f1539bc872813ac14a90521da6248ffbf63913a8675573"; + List addrList = new ArrayList<>(); + addrList.add("1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ"); + JSONArray unspents =new HttpUtil().getUnspent(txid,addrList); + log.info(JSON.toJSONString(unspents)); + } + + + @Test + public void testBTCSign(){ + //unspent所在txid和所在地址 + String txid = "6680c65d3b1f4ab61b7096441a257de51f8bab48d0dca2f76080891ad9b0796a"; + List addrList = new ArrayList<>(); + addrList.add("1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ"); + //可自行设置多个输出地址和金额,找零地址和金额 + JSONArray unspents =new HttpUtil().getUnspent(txid,addrList); + long inputAmount =unspents + .stream() + .mapToLong(o->JSONObject.parseObject(o.toString()).getLong("value")) + .sum(); + + JSONArray outputs = new JSONArray(); + JSONObject outputTo = new JSONObject(); + outputTo.put("address","16SzvWdCrYsVsMuRp43TfqvGvibBah7s17"); + outputTo.put("amount",10000); + outputs.add(outputTo); + long outputToAmount = outputs + .stream() + .mapToLong(o -> JSONObject.parseObject(o.toString()).getLong("amount")) + .sum(); + long outputChangeAmount = inputAmount - outputToAmount; + //input金额不足输出和找零返回不签名 + if (outputChangeAmount<=0){ + return; + } + JSONObject outputChange = new JSONObject(); + outputChange.put("address","1LEHMmGUAzjvMFCoaoUsY46avHzCN3pUdQ"); + outputChange.put("amount",outputChangeAmount); + + outputs.add(outputChange); + + //找零金额小于等于手续费返回不签名 + long fee = (unspents.size() + outputs.size() * 2) * 148 - 10; + if (outputChangeAmount <= fee) { + return; + } + + String signature = new BTCSign().signBTCTransaction("L2xigpefbyesCnzR5hJDFxKPD4A2qSuNHiz4utWnqEovhownw8U2",unspents,outputs); + log.info(signature); + } + + @Test + public void testGetAddress(){ + log.info(new BTCSign().getAddress("d71405cd1c95271e4c17569494100856d9e8706e081702ec5ec91a5b20ce1957")); + } + + +} diff --git a/sign-java-sdk/src/test/java/com/tokenview/sign/test/ETHSignTest.java b/sign-java-sdk/src/test/java/com/tokenview/sign/test/ETHSignTest.java new file mode 100644 index 0000000..8f18b52 --- /dev/null +++ b/sign-java-sdk/src/test/java/com/tokenview/sign/test/ETHSignTest.java @@ -0,0 +1,51 @@ +package com.tokenview.sign.test; + +import com.tokenview.sign.eth.ETHSign; +import com.tokenview.utils.BTCWalletUtil; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class ETHSignTest { + + @Test + public void testGetPrivateKeyFromStrList() { + List stringList = new ArrayList(); + stringList.add("gym"); + stringList.add("please"); + stringList.add("sauce"); + stringList.add("elephant"); + stringList.add("trap"); + stringList.add("bag"); + stringList.add("logic"); + stringList.add("okay"); + stringList.add("impulse"); + stringList.add("slogan"); + stringList.add("goose"); + stringList.add("birth"); + BTCWalletUtil.loadWalletByMnemonic("eth",stringList,",",""); + } + + @Test + public void testGetAddress() { + String address = new ETHSign().getAddress("b71a7616b42110d8345ddc6826ec42c2f1ce24d5f4d8efeb616168d5c1ef4a1f"); + log.info(address); + } + + @Test + public void testETHSign() { + + String sinature = new ETHSign().signETHTransaction( + "0xb71a7616b42110d8345ddc6826ec42c2f1ce24d5f4d8efeb616168d5c1ef4a1f", + "0x9Ae75431335d2e70f8DB0b35F6C179a43756f78e", + "1", + 78500000000L, + 21000L, + 80000000000L); + log.info(sinature); + } + +}