├── .gitignore ├── CMC └── WhaleEx_API_Endpoint.MD ├── README.MD ├── WhaleEx_API_ZH.pdf ├── WhaleEx_API_ZH.v1.0.1.pdf ├── WhaleEx_API_ZH.v1.0.2.pdf ├── WhaleEx_API_ZH.v1.0.3.pdf └── sample ├── java ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── whaleex │ │ │ └── api │ │ │ └── client │ │ │ ├── JavaClientApplication.java │ │ │ ├── WhaleexApiService.java │ │ │ ├── constant │ │ │ └── WhaleexConstant.java │ │ │ ├── impl │ │ │ └── WhaleexApiServiceImpl.java │ │ │ ├── pojo │ │ │ ├── KeyPair.java │ │ │ ├── StringPair.java │ │ │ ├── Symbol.java │ │ │ ├── request │ │ │ │ ├── AutoOrderRequest.java │ │ │ │ └── RequestBody.java │ │ │ └── response │ │ │ │ ├── AssetSummaryResponse.java │ │ │ │ ├── CommonResponse.java │ │ │ │ ├── CurrentOrderResponse.java │ │ │ │ ├── GlobalIdResponse.java │ │ │ │ ├── PageResponse.java │ │ │ │ ├── PublicKeyResponse.java │ │ │ │ ├── SimpleOrderBookResponse.java │ │ │ │ ├── SimplePriceLevelResponse.java │ │ │ │ ├── SymbolResponse.java │ │ │ │ ├── TokenResponse.java │ │ │ │ └── TradeResponse.java │ │ │ └── util │ │ │ ├── Ecc.java │ │ │ ├── HashUtils.java │ │ │ ├── HttpUtils.java │ │ │ ├── JsonUtils.java │ │ │ ├── SignUtils.java │ │ │ ├── StoreUtils.java │ │ │ ├── crypto │ │ │ ├── Hmac.java │ │ │ ├── digest │ │ │ │ ├── GeneralDigest.java │ │ │ │ ├── Ripemd160.java │ │ │ │ ├── Sha256.java │ │ │ │ └── Sha512.java │ │ │ ├── ec │ │ │ │ ├── CurveParam.java │ │ │ │ ├── EcCurve.java │ │ │ │ ├── EcDsa.java │ │ │ │ ├── EcFieldElement.java │ │ │ │ ├── EcPoint.java │ │ │ │ ├── EcSignature.java │ │ │ │ ├── EcTools.java │ │ │ │ ├── EosEcUtil.java │ │ │ │ ├── EosPrivateKey.java │ │ │ │ └── EosPublicKey.java │ │ │ └── util │ │ │ │ ├── Base58.java │ │ │ │ ├── BitUtils.java │ │ │ │ ├── CryptUtil.java │ │ │ │ ├── HexUtils.java │ │ │ │ └── RefValue.java │ │ │ └── types │ │ │ ├── EosByteWriter.java │ │ │ ├── EosType.java │ │ │ ├── TypeAccountName.java │ │ │ ├── TypeActionName.java │ │ │ ├── TypeChainId.java │ │ │ ├── TypeName.java │ │ │ ├── TypePermissionLevel.java │ │ │ └── TypePermissionName.java │ └── resources │ │ └── keys.properties │ └── test │ └── java │ └── com │ └── whaleex │ └── api │ └── client │ └── util │ ├── ApiServiceTest.java │ ├── HttpUtilsTest.java │ ├── PasswordUtilsTest.java │ ├── SignUtilsTest.java │ └── StoreUtilsTest.java └── nodejs ├── ApiKey.json ├── config.json ├── index.js ├── package.json ├── util ├── api.js ├── console.js └── country.json ├── whaleex-api.js └── whaleex-sign.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | sample/nodejs/node_modules/ 3 | -------------------------------------------------------------------------------- /CMC/WhaleEx_API_Endpoint.MD: -------------------------------------------------------------------------------- 1 | Whaleex public market related api endpoint 2 | ================= 3 | 4 | #### Endpoint 1 : https://www.whaleex.com/BUSINESS/api/v2/public/assets 5 | 6 | |S/N |Field Name |Example(s) |Status| 7 | |----|----|----|----| 8 | |1 |`` |BTC, ETH | Mandatory| 9 | |2 | lastUpdateTimestamp |2019-06-14T16:46:10.000Z |Mandatory| 10 | |3 | name |Bitcoin, Ethereum |Mandatory | 11 | |4 | id |1 |Recommended| 12 | |5 | canWithdraw| True/False |Recommended| 13 | |6 | canDeposit | True/False |Recommended| 14 | |7 | minWithdrawal | 0.01 |Recommended| 15 | 16 | 17 | #### Endpoint 2: https://www.whaleex.com/BUSINESS/api/v2/public/ticker 18 | 19 | | S/N | Field Name | Example(s) | Status | 20 | | ---- | ---- | ---- | ---- | 21 | |1 |lastUpdateTimestamp |2019-06-14T16:46:10.000Z |Mandatory| 22 | |2 |tradingPairs (base_quote format) |ETH_BTC |Mandatory| 23 | |3 |LastPrice |0.01 |Mandatory| 24 | |4 |lowestAsk |0.01 |Mandatory| 25 | |5 |highestBid |0.01 |Mandatory| 26 | |6 |baseVolume24h |10000 |Mandatory| 27 | |7 |quoteVolume24h |100 |Mandatory| 28 | |8 |tradesEnabled |True/False |Mandatory| 29 | 30 | 31 | #### Endpoint 3: https://www.whaleex.com/BUSINESS/api/v2/public/trades/[MarketPair] 32 | 33 | | S/N | Field Name | Example(s) | Status | 34 | | ---- | ---- | ---- | ---- | 35 | |1 |`[MarketPair]` |ETH_BTC |Mandatory| 36 | |2 |tradeID (generated by exchange) |28457 |Mandatory| 37 | |3 |price |0.01 |Mandatory| 38 | |4 |baseVolume |50 |Mandatory| 39 | |5 |quoteVolume |1.2 |Mandatory| 40 | |6 |time |2019-06-14T16:46:10.000Z |Mandatory| 41 | |7 |isBuyerMaker |True/False |Mandatory| 42 | 43 | 44 | #### Endpoint 4: https://www.whaleex.com/BUSINESS/api/v2/public/depth/[MarketPair] 45 | 46 | | S/N | Field Name | Example(s) | Status | 47 | | ---- | ---- | ---- | ---- | 48 | |1 |lastUpdateTimestamp |2019-06-14T16:46:10.000Z |Mandatory| 49 | |2 |bids: |[PRICE] |4.00000000 |Mandatory| 50 | |3 |bids: |[QTY] |431.00000000 |Mandatory| 51 | |4 |asks: |[PRICE] |4.00000200 |Mandatory| 52 | |5 |asks: |[QTY] |12.00000000 |Mandatory| 53 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # WhaleEx API Document 2 | This repository is for API documentation of WhaleEx ([whaleex.com](https://www.whaleex.com)). 3 | 4 | The API is ready to use, but the document and samples are not finalized yet. 5 | For experienced developers, or developers might not understand Chinese, please refer to sample/nodejs code. 6 | 7 | We will refine this repository in the following weeks. 8 | 9 | -------------------------------------------------------------------------------- /WhaleEx_API_ZH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/WhaleEx_API_ZH.pdf -------------------------------------------------------------------------------- /WhaleEx_API_ZH.v1.0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/WhaleEx_API_ZH.v1.0.1.pdf -------------------------------------------------------------------------------- /WhaleEx_API_ZH.v1.0.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/WhaleEx_API_ZH.v1.0.2.pdf -------------------------------------------------------------------------------- /WhaleEx_API_ZH.v1.0.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/WhaleEx_API_ZH.v1.0.3.pdf -------------------------------------------------------------------------------- /sample/java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.0.2.RELEASE 9 | 10 | 11 | 12 | whaleex 13 | java-client 14 | 0.0.0 15 | 4.0.0 16 | 17 | 18 | 19 | com.fasterxml.jackson.core 20 | jackson-core 21 | 2.9.7 22 | 23 | 24 | com.fasterxml.jackson.core 25 | jackson-annotations 26 | 2.9.7 27 | 28 | 29 | com.fasterxml.jackson.core 30 | jackson-databind 31 | 2.9.7 32 | 33 | 34 | 35 | org.apache.httpcomponents 36 | httpclient 37 | 4.5.6 38 | 39 | 40 | commons-logging 41 | commons-logging 42 | 1.2 43 | 44 | 45 | commons-io 46 | commons-io 47 | 2.6 48 | 49 | 50 | commons-lang 51 | commons-lang 52 | 2.6 53 | 54 | 55 | com.google.guava 56 | guava 57 | 26.0-jre 58 | 59 | 60 | 61 | junit 62 | junit 63 | 4.12 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-surefire-plugin 74 | 2.21.0 75 | 76 | true 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-compiler-plugin 82 | 3.1 83 | 84 | 1.8 85 | 1.8 86 | 87 | /src/test/** 88 | 89 | utf-8 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/JavaClientApplication.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client; 2 | 3 | import com.whaleex.api.client.constant.WhaleexConstant; 4 | import com.whaleex.api.client.impl.WhaleexApiServiceImpl; 5 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 6 | import com.whaleex.api.client.pojo.response.*; 7 | import com.whaleex.api.client.util.StoreUtils; 8 | import org.apache.commons.lang.StringUtils; 9 | 10 | import java.io.IOException; 11 | import java.util.*; 12 | 13 | public class JavaClientApplication { 14 | 15 | 16 | private static WhaleexApiService apiService = new WhaleexApiServiceImpl(); 17 | 18 | public static void main(String[] args) throws IOException, InterruptedException { 19 | 20 | Scanner sc = new Scanner(System.in); 21 | System.out.println(" 请输入whaleex 用户名:"); 22 | String phone = sc.nextLine(); 23 | System.out.println(" 请输入whaleex 密码:"); 24 | String password = sc.nextLine(); 25 | System.out.println(" 请输入国家代码 (中国: CN):"); 26 | String countryCode = sc.nextLine(); 27 | 28 | String userToken = apiService.getUserToken(phone, password, countryCode); 29 | 30 | final List check = onStartUp(); 31 | if(check.size()>0){ 32 | 33 | if(StringUtils.isEmpty(userToken)){ 34 | System.out.println("密码错误"); 35 | System.exit(0); 36 | } 37 | System.out.println(" 登录成功 "); 38 | 39 | if(check.contains("APIKey")){ 40 | //没有apiKey 申请一个 41 | CommonResponse apiKeyResponse = apiService.generateApiKey(userToken); 42 | if(StringUtils.isNotEmpty(apiKeyResponse.getResult().toString())){ 43 | System.out.println(" apiKey 生成成功"); 44 | StoreUtils.storeAPIKey(apiKeyResponse.getResult().toString()); 45 | } 46 | } 47 | if(check.contains("privateKey")){ 48 | genAndRegPk(userToken); 49 | } 50 | 51 | } 52 | 53 | printBindHelpMsg(); 54 | System.out.println(" 是否已完成转账? Y/N"); 55 | String s = sc.nextLine(); 56 | if(s.equalsIgnoreCase("Y")||s.equalsIgnoreCase("YES")) { 57 | loopPkStatus(userToken); 58 | } 59 | 60 | showSymbolList(); 61 | showAssets(); 62 | showOpenOrders(); 63 | showOrderBook("IQEBTC"); 64 | 65 | 66 | CommonResponse idFromServer = apiService.getIdFromServer(0, 5); 67 | if(idFromServer != null){ 68 | GlobalIdResponse idResult = idFromServer.getResult(); 69 | System.out.println(" 开始下单 ..."); 70 | idResult.getList().stream().forEach(orderId->{ 71 | 72 | String account =WhaleexConstant.USER_EOSACCOUNT; 73 | AutoOrderRequest orderRequest = new AutoOrderRequest(); 74 | orderRequest.setType(AutoOrderRequest.OrderType.sell_market); 75 | orderRequest.setAmount("100"); 76 | orderRequest.setPrice("0.01"); 77 | orderRequest.setSymbol("IQEBTC"); 78 | 79 | try { 80 | CommonResponse orderResponse = apiService.placeOrder(Long.valueOf(orderId), account, orderRequest); 81 | 82 | System.out.println(orderResponse); 83 | } catch (IOException e) { 84 | e.printStackTrace(); 85 | } 86 | }); 87 | 88 | System.out.println(" 撤单.."); 89 | CommonResponse cancelResp = apiService.cancelOpenOrders(); 90 | System.out.println(cancelResp.toString()); 91 | 92 | 93 | }else { 94 | System.err.println(" 系统错误 "); 95 | System.exit(0); 96 | } 97 | 98 | 99 | 100 | } 101 | 102 | 103 | private static void showAssets() throws IOException { 104 | CommonResponse assetsResponse = apiService.queryAssets(); 105 | AssetSummaryResponse assetSummary = assetsResponse.getResult(); 106 | if(assetSummary != null){ 107 | AssetSummaryResponse.Whale wal = assetSummary.getWal(); 108 | System.out.println("WAL 资产 "); 109 | System.out.println("可用: "+wal.getFreeAmount()); 110 | System.out.println("私募: "+wal.getPrivatePlacement()); 111 | System.out.println("锁仓: "+wal.getStakeAmount()); 112 | System.out.println("解锁中: "+wal.getUnStakingAmount()); 113 | 114 | List assetContents = Optional.ofNullable(assetSummary.getList().getContent()) 115 | .orElseGet(ArrayList::new); 116 | assetContents.forEach(asset->{ 117 | System.out.println(" 币种 : "+asset.getCurrency()); 118 | System.out.println(" 可用 : "+asset.getAvailableAmount()); 119 | System.out.println(" 持仓 : "+asset.getFixedAmount()); 120 | System.out.println(" 冻结 : "+asset.getFrozenAmount()); 121 | System.out.println(" 私募 : "+asset.getPrivatePlacement()); 122 | System.out.println(" 总计 : "+asset.getTotalAmount()); 123 | System.out.println("---------------------"); 124 | }); 125 | } 126 | 127 | } 128 | 129 | 130 | private static void showSymbolList() throws IOException { 131 | System.out.println(" 交易对信息"); 132 | List symbolResponse =Optional.ofNullable( apiService.getSymbol()).orElseGet(ArrayList::new); 133 | symbolResponse.forEach(symbol->{ 134 | System.out.println(" baseCurrency "+symbol.getBaseCurrency() +" | basePrecision "+symbol.getBasePrecision()+ 135 | " | quoteCurrency "+symbol.getQuoteCurrency()+" | quotePrecision "+symbol.getQuotePrecision()); 136 | }); 137 | System.out.println("-------------------"); 138 | 139 | } 140 | 141 | private static void genAndRegPk(String accessToken) throws IOException { 142 | System.out.println(" 生成本地公私钥..."); 143 | apiService.generateKeys(); 144 | apiService.registerPK(accessToken,StoreUtils.getStorePublicKey()); 145 | } 146 | 147 | private static void printBindHelpMsg(){ 148 | 149 | System.out.println(" 本地公私钥注册成功 "); 150 | System.out.println(" 请向合约账户转账小额eos资产以激活公钥 "); 151 | System.out.println("合约账户: "+WhaleexConstant.EX_ACCOUNT); 152 | System.out.println("memo: bind:"+StoreUtils.getStorePublicKey()+":WhaleEx"); 153 | } 154 | 155 | private static void showOrderBook(String symbol) throws IOException { 156 | SimpleOrderBookResponse orderBook = apiService.getOrderBook(symbol); 157 | System.out.println("买盘:"); 158 | System.out.println("------------"); 159 | orderBook.getAsks().stream().sorted(Comparator.comparing(a->a.getPrice())).forEach(as->{ 160 | System.out.println("价格:\t"+as.getPrice()+"\t数量:"+as.getQuantity()); 161 | }); 162 | System.out.println("##############"); 163 | System.out.println("卖盘:"); 164 | System.out.println("------------"); 165 | orderBook.getBids().stream().sorted(Comparator.comparing(a->a.getPrice())).forEach(as->{ 166 | System.out.println("价格:\t"+as.getPrice()+"\t数量:"+as.getQuantity()); 167 | }); 168 | } 169 | 170 | private static void loopPkStatus(String userToken) throws InterruptedException { 171 | System.out.print("检查"+StoreUtils.getStorePublicKey()+"公钥绑定状态."); 172 | while (!"ACTIVED".equals(checkPublicKeyStatus(userToken))){ 173 | System.out.print("."); 174 | Thread.sleep(5000l); 175 | 176 | } 177 | System.out.println(""); 178 | System.out.println(" 公钥 "+StoreUtils.getStorePublicKey()+" 已绑定"); 179 | } 180 | 181 | private static void showOpenOrders() throws IOException { 182 | System.out.println(" 当前委托订单: "); 183 | CommonResponse> openOrders = apiService.getOpenOrders(); 184 | if(openOrders != null){ 185 | PageResponse result = openOrders.getResult(); 186 | result.getContent().forEach(order->{ 187 | System.out.println("orderId "+order.getOrderId()); 188 | System.out.println("price "+order.getPrice()); 189 | System.out.println("createTime "+order.getCreateTime()); 190 | System.out.println("type "+order.getType()); 191 | System.out.println("---------------------"); 192 | 193 | }); 194 | 195 | 196 | } 197 | } 198 | 199 | 200 | 201 | private static String checkPublicKeyStatus(String accessToken){ 202 | String pk = StoreUtils.getStorePublicKey(); 203 | try { 204 | CommonResponse response = apiService.checkPkBindStatus(accessToken, pk); 205 | if(response!= null && response.getResult() != null ){ 206 | return response.getResult().getStatus(); 207 | } 208 | } catch (IOException e) { 209 | e.printStackTrace(); 210 | } 211 | return ""; 212 | 213 | } 214 | 215 | 216 | private static List onStartUp(){ 217 | List checkRes = new ArrayList<>(); 218 | if(StringUtils.isEmpty(StoreUtils.getStorePrivateKey())){ 219 | checkRes.add("privateKey"); 220 | } 221 | if(StringUtils.isEmpty(StoreUtils.getStoreAPIkey())){ 222 | checkRes.add("APIKey"); 223 | } 224 | 225 | return checkRes; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/WhaleexApiService.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client; 2 | 3 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 4 | import com.whaleex.api.client.pojo.response.*; 5 | 6 | import java.io.IOException; 7 | import java.io.UnsupportedEncodingException; 8 | import java.util.List; 9 | 10 | public interface WhaleexApiService { 11 | 12 | /** 13 | * 登录 获取token 14 | */ 15 | String getUserToken(String userName,String password,String countryCode) throws IOException; 16 | 17 | /** 18 | * 生产公私钥 19 | * @return 20 | */ 21 | void generateKeys(); 22 | 23 | /** 24 | * 注册公钥 25 | */ 26 | CommonResponse registerPK(String accessToken,String pk) throws IOException; 27 | 28 | /** 29 | * 检查公钥绑定状态 30 | */ 31 | CommonResponse checkPkBindStatus(String accessToken, String pk) throws IOException; 32 | 33 | /** 34 | * 生成apiKey 35 | * @param accessToken 36 | * @return 37 | */ 38 | CommonResponse generateApiKey(String accessToken) throws IOException; 39 | 40 | /** 41 | * 获取orderId 42 | */ 43 | CommonResponse getIdFromServer(int remark, int size) throws IOException; 44 | 45 | /** 46 | * 下单 47 | */ 48 | CommonResponse placeOrder(Long orderId, String account, AutoOrderRequest order) throws IOException; 49 | 50 | /** 51 | * 撤单 52 | */ 53 | CommonResponse cancelOrder(long orderId) throws IOException; 54 | 55 | /** 56 | * 订单详情 57 | */ 58 | CommonResponse getOrderDetail(long orderId) throws IOException; 59 | 60 | 61 | /** 62 | * 成交详情 63 | */ 64 | CommonResponse> queryOneOrderMatchResults(long orderId) throws IOException; //todo pageimpl 65 | 66 | 67 | /** 68 | * 用户资产 69 | * @return 70 | * @throws IOException 71 | */ 72 | CommonResponse queryAssets() throws IOException; 73 | 74 | /** 75 | * 交易对 76 | * @return 77 | * @throws IOException 78 | */ 79 | List getSymbol() throws IOException; 80 | 81 | CommonResponse> getOpenOrders() throws IOException; 82 | 83 | /** 84 | * 获取订单簿 85 | * @return 86 | */ 87 | SimpleOrderBookResponse getOrderBook(String symbol) throws IOException; 88 | 89 | CommonResponse cancelOpenOrders() throws IOException; 90 | } 91 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/constant/WhaleexConstant.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.constant; 2 | 3 | /** 4 | * Constants used throughout Whaleex's API. 5 | */ 6 | public class WhaleexConstant { 7 | 8 | /** 9 | * REST API base URL. 10 | */ 11 | public static final String API_BASE_URL = "api.whaleex.com"; 12 | public static final String BUSINESS_URL_PREFIX ="https://"+API_BASE_URL+"/BUSINESS"; 13 | public static final String UAA_URL_PREFIX ="https://"+API_BASE_URL+"/UAA"; 14 | public static final String EX_EOSACCOUNT ="whaleexchang"; 15 | public static final String EX_ACCOUNT ="whaleextrust"; 16 | public static final String API_KEY = "APIKey"; 17 | 18 | 19 | public static final String USER_EOSACCOUNT="your_eosaccount"; 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/KeyPair.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo; 2 | 3 | public class KeyPair { 4 | String publicKey; 5 | String privateKey; 6 | 7 | public String getPublicKey() { 8 | return publicKey; 9 | } 10 | 11 | public void setPublicKey(String publicKey) { 12 | this.publicKey = publicKey; 13 | } 14 | 15 | public String getPrivateKey() { 16 | return privateKey; 17 | } 18 | 19 | public void setPrivateKey(String privateKey) { 20 | this.privateKey = privateKey; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/StringPair.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo; 2 | 3 | public class StringPair { 4 | String first; 5 | String second; 6 | 7 | public String getFirst() { 8 | return first; 9 | } 10 | 11 | public void setFirst(String first) { 12 | this.first = first; 13 | } 14 | 15 | public String getSecond() { 16 | return second; 17 | } 18 | 19 | public void setSecond(String second) { 20 | this.second = second; 21 | } 22 | 23 | public StringPair(String first, String second) { 24 | this.first = first; 25 | this.second = second; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/Symbol.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo; 2 | 3 | public class Symbol { 4 | String baseCurrency; 5 | String quoteCurrency; 6 | int basePrecision; 7 | int quotePrecision; 8 | String baseContract; 9 | String quoteContract; 10 | 11 | 12 | public String getBaseCurrency() { 13 | return baseCurrency; 14 | } 15 | 16 | public void setBaseCurrency(String baseCurrency) { 17 | this.baseCurrency = baseCurrency; 18 | } 19 | 20 | public String getQuoteCurrency() { 21 | return quoteCurrency; 22 | } 23 | 24 | public void setQuoteCurrency(String quoteCurrency) { 25 | this.quoteCurrency = quoteCurrency; 26 | } 27 | 28 | public int getBasePrecision() { 29 | return basePrecision; 30 | } 31 | 32 | public void setBasePrecision(int basePrecision) { 33 | this.basePrecision = basePrecision; 34 | } 35 | 36 | public int getQuotePrecision() { 37 | return quotePrecision; 38 | } 39 | 40 | public void setQuotePrecision(int quotePrecision) { 41 | this.quotePrecision = quotePrecision; 42 | } 43 | 44 | public String getBaseContract() { 45 | return baseContract; 46 | } 47 | 48 | public void setBaseContract(String baseContract) { 49 | this.baseContract = baseContract; 50 | } 51 | 52 | public String getQuoteContract() { 53 | return quoteContract; 54 | } 55 | 56 | public void setQuoteContract(String quoteContract) { 57 | this.quoteContract = quoteContract; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/request/AutoOrderRequest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.request; 2 | 3 | 4 | public class AutoOrderRequest extends RequestBody { 5 | String amount; 6 | String price; 7 | String symbol; 8 | String type; 9 | 10 | public interface OrderType{ 11 | 12 | String sell_limit ="sell-limit"; 13 | String buy_limit ="buy-limit"; 14 | String buy_market ="buy-market"; 15 | String sell_market ="sell-market"; 16 | } 17 | 18 | 19 | public String getAmount() { 20 | return amount; 21 | } 22 | 23 | public void setAmount(String amount) { 24 | this.amount = amount; 25 | } 26 | 27 | public String getPrice() { 28 | return price; 29 | } 30 | 31 | public void setPrice(String price) { 32 | this.price = price; 33 | } 34 | 35 | public String getSymbol() { 36 | return symbol; 37 | } 38 | 39 | public void setSymbol(String symbol) { 40 | this.symbol = symbol; 41 | } 42 | 43 | public String getType() { 44 | return type; 45 | } 46 | 47 | public void setType(String type) { 48 | this.type = type; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/request/RequestBody.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.request; 2 | 3 | public abstract class RequestBody { 4 | } 5 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/AssetSummaryResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | public class AssetSummaryResponse { 4 | 5 | private Whale wal; 6 | private Summary summary; 7 | private PageResponse list; 8 | 9 | 10 | public Whale getWal() { 11 | return wal; 12 | } 13 | 14 | public void setWal(Whale wal) { 15 | this.wal = wal; 16 | } 17 | 18 | public Summary getSummary() { 19 | return summary; 20 | } 21 | 22 | public void setSummary(Summary summary) { 23 | this.summary = summary; 24 | } 25 | 26 | public PageResponse getList() { 27 | return list; 28 | } 29 | 30 | public void setList(PageResponse list) { 31 | this.list = list; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "AssetSummaryResponse{" + 37 | "wal=" + wal + 38 | ", summary=" + summary + 39 | ", list=" + list + 40 | '}'; 41 | } 42 | 43 | public static class AssetContent { 44 | private String currency; 45 | private long currencyId; 46 | private String totalAmount; 47 | private String availableAmount; 48 | private String frozenAmount; 49 | private String fixedAmount; 50 | private String privatePlacement; 51 | 52 | @Override 53 | public String toString() { 54 | return "AssetContent{" + 55 | "currency='" + currency + '\'' + 56 | ", currencyId=" + currencyId + 57 | ", totalAmount='" + totalAmount + '\'' + 58 | ", availableAmount='" + availableAmount + '\'' + 59 | ", frozenAmount='" + frozenAmount + '\'' + 60 | ", fixedAmount='" + fixedAmount + '\'' + 61 | ", privatePlacement='" + privatePlacement + '\'' + 62 | '}'; 63 | } 64 | 65 | public String getCurrency() { 66 | return currency; 67 | } 68 | 69 | public void setCurrency(String currency) { 70 | this.currency = currency; 71 | } 72 | 73 | public long getCurrencyId() { 74 | return currencyId; 75 | } 76 | 77 | public void setCurrencyId(long currencyId) { 78 | this.currencyId = currencyId; 79 | } 80 | 81 | public String getTotalAmount() { 82 | return totalAmount; 83 | } 84 | 85 | public void setTotalAmount(String totalAmount) { 86 | this.totalAmount = totalAmount; 87 | } 88 | 89 | public String getAvailableAmount() { 90 | return availableAmount; 91 | } 92 | 93 | public void setAvailableAmount(String availableAmount) { 94 | this.availableAmount = availableAmount; 95 | } 96 | 97 | public String getFrozenAmount() { 98 | return frozenAmount; 99 | } 100 | 101 | public void setFrozenAmount(String frozenAmount) { 102 | this.frozenAmount = frozenAmount; 103 | } 104 | 105 | public String getFixedAmount() { 106 | return fixedAmount; 107 | } 108 | 109 | public void setFixedAmount(String fixedAmount) { 110 | this.fixedAmount = fixedAmount; 111 | } 112 | 113 | public String getPrivatePlacement() { 114 | return privatePlacement; 115 | } 116 | 117 | public void setPrivatePlacement(String privatePlacement) { 118 | this.privatePlacement = privatePlacement; 119 | } 120 | } 121 | 122 | public static class Summary { 123 | private String amount; 124 | private String shortName; 125 | private boolean withdrawNeedVerify; 126 | 127 | public String getAmount() { 128 | return amount; 129 | } 130 | 131 | public void setAmount(String amount) { 132 | this.amount = amount; 133 | } 134 | 135 | public String getShortName() { 136 | return shortName; 137 | } 138 | 139 | public void setShortName(String shortName) { 140 | this.shortName = shortName; 141 | } 142 | 143 | public boolean isWithdrawNeedVerify() { 144 | return withdrawNeedVerify; 145 | } 146 | 147 | public void setWithdrawNeedVerify(boolean withdrawNeedVerify) { 148 | this.withdrawNeedVerify = withdrawNeedVerify; 149 | } 150 | } 151 | 152 | public static class Whale { 153 | private String freeAmount; 154 | private String stakeAmount; 155 | private String unStakingAmount; 156 | private String privatePlacement; 157 | 158 | public String getFreeAmount() { 159 | return freeAmount; 160 | } 161 | 162 | public void setFreeAmount(String freeAmount) { 163 | this.freeAmount = freeAmount; 164 | } 165 | 166 | public String getStakeAmount() { 167 | return stakeAmount; 168 | } 169 | 170 | public void setStakeAmount(String stakeAmount) { 171 | this.stakeAmount = stakeAmount; 172 | } 173 | 174 | public String getUnStakingAmount() { 175 | return unStakingAmount; 176 | } 177 | 178 | public void setUnStakingAmount(String unStakingAmount) { 179 | this.unStakingAmount = unStakingAmount; 180 | } 181 | 182 | public String getPrivatePlacement() { 183 | return privatePlacement; 184 | } 185 | 186 | public void setPrivatePlacement(String privatePlacement) { 187 | this.privatePlacement = privatePlacement; 188 | } 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/CommonResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | public class CommonResponse { 4 | private String returnCode; 5 | private String message; 6 | private String errorCode; 7 | 8 | private T result; 9 | 10 | public String getReturnCode() { 11 | return returnCode; 12 | } 13 | 14 | public void setReturnCode(String returnCode) { 15 | this.returnCode = returnCode; 16 | } 17 | 18 | public String getMessage() { 19 | return message; 20 | } 21 | 22 | public void setMessage(String message) { 23 | this.message = message; 24 | } 25 | 26 | public String getErrorCode() { 27 | return errorCode; 28 | } 29 | 30 | public void setErrorCode(String errorCode) { 31 | this.errorCode = errorCode; 32 | } 33 | 34 | public T getResult() { 35 | return result; 36 | } 37 | 38 | public void setResult(T result) { 39 | this.result = result; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "CommonResponse{" + 45 | "returnCode='" + returnCode + '\'' + 46 | ", message='" + message + '\'' + 47 | ", errorCode='" + errorCode + '\'' + 48 | ", result=" + result + 49 | '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/CurrentOrderResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | 4 | public class CurrentOrderResponse { 5 | 6 | private String orderId; 7 | 8 | private short symbolId; 9 | private byte status; 10 | /** 委托价 **/ 11 | private String price; 12 | /** 委托量 **/ 13 | private String origQty;// 14 | /** 成交总额 **/ 15 | private String execValue;// 16 | /** 成交均价 **/ 17 | private String execAvgPrice;// 18 | /** 成交量 **/ 19 | private String execQty; //成交量 20 | private long createTime; 21 | private String type; 22 | 23 | private long resignTime; //重签名时间 24 | private byte side; 25 | 26 | private short takerFeeRate; 27 | private short makerFeeRate; 28 | 29 | @Override 30 | public String toString() { 31 | return "CurrentOrderResponse{" + 32 | "orderId='" + orderId + '\'' + 33 | ", symbolId=" + symbolId + 34 | ", status=" + status + 35 | ", price='" + price + '\'' + 36 | ", origQty='" + origQty + '\'' + 37 | ", execValue='" + execValue + '\'' + 38 | ", execAvgPrice='" + execAvgPrice + '\'' + 39 | ", execQty='" + execQty + '\'' + 40 | ", createTime=" + createTime + 41 | ", type='" + type + '\'' + 42 | ", resignTime=" + resignTime + 43 | ", side=" + side + 44 | ", takerFeeRate=" + takerFeeRate + 45 | ", makerFeeRate=" + makerFeeRate + 46 | '}'; 47 | } 48 | 49 | public String getOrderId() { 50 | return orderId; 51 | } 52 | 53 | public void setOrderId(String orderId) { 54 | this.orderId = orderId; 55 | } 56 | 57 | public short getSymbolId() { 58 | return symbolId; 59 | } 60 | 61 | public void setSymbolId(short symbolId) { 62 | this.symbolId = symbolId; 63 | } 64 | 65 | public byte getStatus() { 66 | return status; 67 | } 68 | 69 | public void setStatus(byte status) { 70 | this.status = status; 71 | } 72 | 73 | public String getPrice() { 74 | return price; 75 | } 76 | 77 | public void setPrice(String price) { 78 | this.price = price; 79 | } 80 | 81 | public String getOrigQty() { 82 | return origQty; 83 | } 84 | 85 | public void setOrigQty(String origQty) { 86 | this.origQty = origQty; 87 | } 88 | 89 | public String getExecValue() { 90 | return execValue; 91 | } 92 | 93 | public void setExecValue(String execValue) { 94 | this.execValue = execValue; 95 | } 96 | 97 | public String getExecAvgPrice() { 98 | return execAvgPrice; 99 | } 100 | 101 | public void setExecAvgPrice(String execAvgPrice) { 102 | this.execAvgPrice = execAvgPrice; 103 | } 104 | 105 | public String getExecQty() { 106 | return execQty; 107 | } 108 | 109 | public void setExecQty(String execQty) { 110 | this.execQty = execQty; 111 | } 112 | 113 | public long getCreateTime() { 114 | return createTime; 115 | } 116 | 117 | public void setCreateTime(long createTime) { 118 | this.createTime = createTime; 119 | } 120 | 121 | public String getType() { 122 | return type; 123 | } 124 | 125 | public void setType(String type) { 126 | this.type = type; 127 | } 128 | 129 | public long getResignTime() { 130 | return resignTime; 131 | } 132 | 133 | public void setResignTime(long resignTime) { 134 | this.resignTime = resignTime; 135 | } 136 | 137 | public byte getSide() { 138 | return side; 139 | } 140 | 141 | public void setSide(byte side) { 142 | this.side = side; 143 | } 144 | 145 | public short getTakerFeeRate() { 146 | return takerFeeRate; 147 | } 148 | 149 | public void setTakerFeeRate(short takerFeeRate) { 150 | this.takerFeeRate = takerFeeRate; 151 | } 152 | 153 | public short getMakerFeeRate() { 154 | return makerFeeRate; 155 | } 156 | 157 | public void setMakerFeeRate(short makerFeeRate) { 158 | this.makerFeeRate = makerFeeRate; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/GlobalIdResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | import java.util.List; 4 | 5 | public class GlobalIdResponse { 6 | 7 | private String remark; 8 | List list; 9 | 10 | public String getRemark() { 11 | return remark; 12 | } 13 | 14 | public void setRemark(String remark) { 15 | this.remark = remark; 16 | } 17 | 18 | public List getList() { 19 | return list; 20 | } 21 | 22 | public void setList(List list) { 23 | this.list = list; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "GlobalIdResponse{" + 29 | "remark='" + remark + '\'' + 30 | ", list=" + list + 31 | '}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/PageResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | import java.util.List; 4 | 5 | public class PageResponse { 6 | List content; 7 | Integer totalElements; 8 | 9 | public List getContent() { 10 | return content; 11 | } 12 | 13 | public void setContent(List content) { 14 | this.content = content; 15 | } 16 | 17 | public Integer getTotalElements() { 18 | return totalElements; 19 | } 20 | 21 | public void setTotalElements(Integer totalElements) { 22 | this.totalElements = totalElements; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "PageResponse{" + 28 | "content=" + content + 29 | ", totalElements=" + totalElements + 30 | '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/PublicKeyResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | 4 | 5 | public class PublicKeyResponse { 6 | private String pk; 7 | private String deviceInfo; 8 | private String deviceTag; 9 | private String status; 10 | private Long createdTime; 11 | 12 | public String getPk() { 13 | return pk; 14 | } 15 | 16 | public void setPk(String pk) { 17 | this.pk = pk; 18 | } 19 | 20 | public String getDeviceInfo() { 21 | return deviceInfo; 22 | } 23 | 24 | public void setDeviceInfo(String deviceInfo) { 25 | this.deviceInfo = deviceInfo; 26 | } 27 | 28 | public String getDeviceTag() { 29 | return deviceTag; 30 | } 31 | 32 | public void setDeviceTag(String deviceTag) { 33 | this.deviceTag = deviceTag; 34 | } 35 | 36 | public String getStatus() { 37 | return status; 38 | } 39 | 40 | public void setStatus(String status) { 41 | this.status = status; 42 | } 43 | 44 | public Long getCreatedTime() { 45 | return createdTime; 46 | } 47 | 48 | public void setCreatedTime(Long createdTime) { 49 | this.createdTime = createdTime; 50 | } 51 | } -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/SimpleOrderBookResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | import java.util.List; 4 | 5 | public class SimpleOrderBookResponse { 6 | private String lastUpdateId; 7 | private long timestamp; 8 | List asks; 9 | List bids; 10 | 11 | @Override 12 | public String toString() { 13 | return "SimpleOrderBookResponse{" + 14 | "lastUpdateId='" + lastUpdateId + '\'' + 15 | ", timestamp=" + timestamp + 16 | ", asks=" + asks + 17 | ", bids=" + bids + 18 | '}'; 19 | } 20 | 21 | public String getLastUpdateId() { 22 | return lastUpdateId; 23 | } 24 | 25 | public void setLastUpdateId(String lastUpdateId) { 26 | this.lastUpdateId = lastUpdateId; 27 | } 28 | 29 | public long getTimestamp() { 30 | return timestamp; 31 | } 32 | 33 | public void setTimestamp(long timestamp) { 34 | this.timestamp = timestamp; 35 | } 36 | 37 | public List getAsks() { 38 | return asks; 39 | } 40 | 41 | public void setAsks(List asks) { 42 | this.asks = asks; 43 | } 44 | 45 | public List getBids() { 46 | return bids; 47 | } 48 | 49 | public void setBids(List bids) { 50 | this.bids = bids; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/SimplePriceLevelResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | 4 | public class SimplePriceLevelResponse { 5 | private String price; 6 | private String quantity; 7 | 8 | public String getPrice() { 9 | return price; 10 | } 11 | 12 | public void setPrice(String price) { 13 | this.price = price; 14 | } 15 | 16 | public String getQuantity() { 17 | return quantity; 18 | } 19 | 20 | public void setQuantity(String quantity) { 21 | this.quantity = quantity; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/SymbolResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | 4 | public class SymbolResponse{ 5 | 6 | private String name; 7 | 8 | private long baseCurrencyId; 9 | private String baseCurrency; 10 | private short basePrecision; 11 | 12 | private short quotePrecision; 13 | private long quoteCurrencyId; 14 | private String quoteCurrency; 15 | 16 | 17 | @Override 18 | public String toString() { 19 | return "SymbolResponse{" + 20 | "name='" + name + '\'' + 21 | ", baseCurrencyId=" + baseCurrencyId + 22 | ", baseCurrency='" + baseCurrency + '\'' + 23 | ", basePrecision=" + basePrecision + 24 | ", quotePrecision=" + quotePrecision + 25 | ", quoteCurrencyId=" + quoteCurrencyId + 26 | ", quoteCurrency='" + quoteCurrency + '\'' + 27 | '}'; 28 | } 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public long getBaseCurrencyId() { 39 | return baseCurrencyId; 40 | } 41 | 42 | public void setBaseCurrencyId(long baseCurrencyId) { 43 | this.baseCurrencyId = baseCurrencyId; 44 | } 45 | 46 | public String getBaseCurrency() { 47 | return baseCurrency; 48 | } 49 | 50 | public void setBaseCurrency(String baseCurrency) { 51 | this.baseCurrency = baseCurrency; 52 | } 53 | 54 | public short getBasePrecision() { 55 | return basePrecision; 56 | } 57 | 58 | public void setBasePrecision(short basePrecision) { 59 | this.basePrecision = basePrecision; 60 | } 61 | 62 | public short getQuotePrecision() { 63 | return quotePrecision; 64 | } 65 | 66 | public void setQuotePrecision(short quotePrecision) { 67 | this.quotePrecision = quotePrecision; 68 | } 69 | 70 | public long getQuoteCurrencyId() { 71 | return quoteCurrencyId; 72 | } 73 | 74 | public void setQuoteCurrencyId(long quoteCurrencyId) { 75 | this.quoteCurrencyId = quoteCurrencyId; 76 | } 77 | 78 | public String getQuoteCurrency() { 79 | return quoteCurrency; 80 | } 81 | 82 | public void setQuoteCurrency(String quoteCurrency) { 83 | this.quoteCurrency = quoteCurrency; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/TokenResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | public class TokenResponse { 4 | String access_token; 5 | String token_type; 6 | Long expires_in; 7 | String scope; 8 | Integer groupId; 9 | Long id; 10 | boolean isAdmin; 11 | boolean byPassword; 12 | boolean hasPassword; 13 | String type; 14 | String jti; 15 | 16 | 17 | public String getAccess_token() { 18 | return access_token; 19 | } 20 | 21 | public void setAccess_token(String access_token) { 22 | this.access_token = access_token; 23 | } 24 | 25 | public String getToken_type() { 26 | return token_type; 27 | } 28 | 29 | public void setToken_type(String token_type) { 30 | this.token_type = token_type; 31 | } 32 | 33 | public Long getExpires_in() { 34 | return expires_in; 35 | } 36 | 37 | public void setExpires_in(Long expires_in) { 38 | this.expires_in = expires_in; 39 | } 40 | 41 | public String getScope() { 42 | return scope; 43 | } 44 | 45 | public void setScope(String scope) { 46 | this.scope = scope; 47 | } 48 | 49 | public Integer getGroupId() { 50 | return groupId; 51 | } 52 | 53 | public void setGroupId(Integer groupId) { 54 | this.groupId = groupId; 55 | } 56 | 57 | public Long getId() { 58 | return id; 59 | } 60 | 61 | public void setId(Long id) { 62 | this.id = id; 63 | } 64 | 65 | public boolean isAdmin() { 66 | return isAdmin; 67 | } 68 | 69 | public void setAdmin(boolean admin) { 70 | isAdmin = admin; 71 | } 72 | 73 | public boolean isByPassword() { 74 | return byPassword; 75 | } 76 | 77 | public void setByPassword(boolean byPassword) { 78 | this.byPassword = byPassword; 79 | } 80 | 81 | public boolean isHasPassword() { 82 | return hasPassword; 83 | } 84 | 85 | public void setHasPassword(boolean hasPassword) { 86 | this.hasPassword = hasPassword; 87 | } 88 | 89 | public String getType() { 90 | return type; 91 | } 92 | 93 | public void setType(String type) { 94 | this.type = type; 95 | } 96 | 97 | public String getJti() { 98 | return jti; 99 | } 100 | 101 | public void setJti(String jti) { 102 | this.jti = jti; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/pojo/response/TradeResponse.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.pojo.response; 2 | 3 | 4 | public class TradeResponse { 5 | private String orderId; 6 | private String execId; 7 | private byte type; 8 | private String quoteCurrencyName; 9 | private String baseCurrencyName; 10 | private String price; 11 | private String quantity; 12 | private String volume; 13 | private byte side; 14 | private String status; 15 | private String feeQty; 16 | private long feeCurrencyId; 17 | private String feeCurrency; 18 | private byte liquidityFlag; 19 | private long timestamp; 20 | 21 | 22 | public String getOrderId() { 23 | return orderId; 24 | } 25 | 26 | public void setOrderId(String orderId) { 27 | this.orderId = orderId; 28 | } 29 | 30 | public String getExecId() { 31 | return execId; 32 | } 33 | 34 | public void setExecId(String execId) { 35 | this.execId = execId; 36 | } 37 | 38 | public byte getType() { 39 | return type; 40 | } 41 | 42 | public void setType(byte type) { 43 | this.type = type; 44 | } 45 | 46 | public String getQuoteCurrencyName() { 47 | return quoteCurrencyName; 48 | } 49 | 50 | public void setQuoteCurrencyName(String quoteCurrencyName) { 51 | this.quoteCurrencyName = quoteCurrencyName; 52 | } 53 | 54 | public String getBaseCurrencyName() { 55 | return baseCurrencyName; 56 | } 57 | 58 | public void setBaseCurrencyName(String baseCurrencyName) { 59 | this.baseCurrencyName = baseCurrencyName; 60 | } 61 | 62 | public String getPrice() { 63 | return price; 64 | } 65 | 66 | public void setPrice(String price) { 67 | this.price = price; 68 | } 69 | 70 | public String getQuantity() { 71 | return quantity; 72 | } 73 | 74 | public void setQuantity(String quantity) { 75 | this.quantity = quantity; 76 | } 77 | 78 | public String getVolume() { 79 | return volume; 80 | } 81 | 82 | public void setVolume(String volume) { 83 | this.volume = volume; 84 | } 85 | 86 | public byte getSide() { 87 | return side; 88 | } 89 | 90 | public void setSide(byte side) { 91 | this.side = side; 92 | } 93 | 94 | public String getStatus() { 95 | return status; 96 | } 97 | 98 | public void setStatus(String status) { 99 | this.status = status; 100 | } 101 | 102 | public String getFeeQty() { 103 | return feeQty; 104 | } 105 | 106 | public void setFeeQty(String feeQty) { 107 | this.feeQty = feeQty; 108 | } 109 | 110 | public long getFeeCurrencyId() { 111 | return feeCurrencyId; 112 | } 113 | 114 | public void setFeeCurrencyId(long feeCurrencyId) { 115 | this.feeCurrencyId = feeCurrencyId; 116 | } 117 | 118 | public String getFeeCurrency() { 119 | return feeCurrency; 120 | } 121 | 122 | public void setFeeCurrency(String feeCurrency) { 123 | this.feeCurrency = feeCurrency; 124 | } 125 | 126 | public byte getLiquidityFlag() { 127 | return liquidityFlag; 128 | } 129 | 130 | public void setLiquidityFlag(byte liquidityFlag) { 131 | this.liquidityFlag = liquidityFlag; 132 | } 133 | 134 | public long getTimestamp() { 135 | return timestamp; 136 | } 137 | 138 | public void setTimestamp(long timestamp) { 139 | this.timestamp = timestamp; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/Ecc.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.util.crypto.digest.Sha256; 4 | import com.whaleex.api.client.util.crypto.ec.EcDsa; 5 | import com.whaleex.api.client.util.crypto.ec.EcSignature; 6 | import com.whaleex.api.client.util.crypto.ec.EosPrivateKey; 7 | 8 | public class Ecc { 9 | 10 | public static String Sign(byte[] data,String privateKey){ 11 | EosPrivateKey eosPrivateKey = new EosPrivateKey(privateKey); 12 | EcSignature signature = EcDsa.sign(Sha256.from(data), eosPrivateKey); 13 | return signature.toString(); 14 | } 15 | 16 | public static String Sign(String data,String privateKey){ 17 | EosPrivateKey eosPrivateKey = new EosPrivateKey(privateKey); 18 | EcSignature signature = EcDsa.sign(Sha256.from(data.getBytes()), eosPrivateKey); 19 | return signature.toString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/HashUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.util.crypto.util.HexUtils; 4 | 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | public class HashUtils { 9 | 10 | public static String getHash(String base) { 11 | try{ 12 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 13 | byte[] hash = digest.digest(base.getBytes("UTF-8")); 14 | StringBuffer hexString = new StringBuffer(); 15 | 16 | for (int i = 0; i < hash.length; i++) { 17 | String hex = Integer.toHexString(0xff & hash[i]); 18 | if(hex.length() == 1) hexString.append('0'); 19 | hexString.append(hex); 20 | } 21 | 22 | return hexString.toString(); 23 | } catch(Exception ex){ 24 | throw new RuntimeException(ex); 25 | } 26 | } 27 | 28 | 29 | public static String sha256String(byte[] bytes) { 30 | MessageDigest sha = null; 31 | try { 32 | sha = MessageDigest.getInstance("SHA-256"); 33 | } catch (NoSuchAlgorithmException e) { 34 | e.printStackTrace(); 35 | } 36 | byte[] hashBytes = sha.digest(bytes); 37 | return HexUtils.toHex(hashBytes); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/HttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.constant.WhaleexConstant; 4 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | import org.apache.http.NameValuePair; 8 | import org.apache.http.client.entity.UrlEncodedFormEntity; 9 | import org.apache.http.client.methods.CloseableHttpResponse; 10 | import org.apache.http.client.methods.HttpGet; 11 | import org.apache.http.client.methods.HttpPost; 12 | import org.apache.http.client.methods.HttpUriRequest; 13 | import org.apache.http.entity.StringEntity; 14 | import org.apache.http.impl.client.CloseableHttpClient; 15 | import org.apache.http.impl.client.HttpClients; 16 | import org.apache.http.message.BasicNameValuePair; 17 | import org.apache.http.util.EntityUtils; 18 | 19 | import java.io.IOException; 20 | import java.io.UnsupportedEncodingException; 21 | import java.util.*; 22 | 23 | public class HttpUtils { 24 | 25 | 26 | Log log = LogFactory.getLog(HttpUtils.class); 27 | 28 | 29 | public String doGet(String path,Map params){ 30 | HttpGet httpGet = new HttpGet(WhaleexConstant.BUSINESS_URL_PREFIX+pathBuilder(path,params)); 31 | return executeHttp(httpGet); 32 | 33 | } 34 | public String doGet(String path,Map params,Map headers){ 35 | HttpGet httpGet = new HttpGet(WhaleexConstant.BUSINESS_URL_PREFIX+pathBuilder(path,params)); 36 | if(headers!= null){ 37 | headers.entrySet().forEach(ent->{ 38 | httpGet.addHeader(ent.getKey(),ent.getValue()); 39 | }); 40 | } 41 | return executeHttp(httpGet); 42 | 43 | } 44 | 45 | public String doPost(String path,Map params,Object body){ 46 | HttpPost httpPost = new HttpPost(WhaleexConstant.BUSINESS_URL_PREFIX+pathBuilder(path,params)); 47 | buildPostBody(body, httpPost); 48 | return executeHttp(httpPost); 49 | } 50 | 51 | public String doPost(String path,Map params,Object body,Map headers){ 52 | 53 | HttpPost httpPost = new HttpPost(WhaleexConstant.BUSINESS_URL_PREFIX+pathBuilder(path,params)); 54 | buildPostBody(body, httpPost); 55 | if(headers!= null){ 56 | headers.entrySet().forEach(ent->{ 57 | httpPost.addHeader(ent.getKey(),ent.getValue()); 58 | }); 59 | } 60 | return executeHttp(httpPost); 61 | } 62 | 63 | /** 64 | * 登录 65 | */ 66 | public String doLoginRequest(String userName,String password,String countryCode){ 67 | String path = "/oauth/token"; 68 | HttpPost httpPost = new HttpPost(WhaleexConstant.UAA_URL_PREFIX+path); 69 | 70 | List list = new ArrayList<>(); 71 | list.add(new BasicNameValuePair("grant_type","password")); 72 | list.add(new BasicNameValuePair("client_id","client")); 73 | list.add(new BasicNameValuePair("client_secret","secret")); 74 | list.add(new BasicNameValuePair("clientType","api")); 75 | list.add(new BasicNameValuePair("source","api")); 76 | list.add(new BasicNameValuePair("countryCode",countryCode)); 77 | list.add(new BasicNameValuePair("username",userName)); 78 | 79 | list.add(new BasicNameValuePair("password",HashUtils.getHash(password))); 80 | 81 | try { 82 | UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(list); 83 | httpPost.setEntity(uefEntity); 84 | 85 | return executeHttp(httpPost); 86 | } catch (UnsupportedEncodingException e) { 87 | e.printStackTrace(); 88 | } 89 | 90 | return null; 91 | } 92 | 93 | 94 | 95 | private String pathBuilder(String path,Map params){ 96 | if(params == null || params.size() ==0){ 97 | return path; 98 | } 99 | StringBuilder pathBuilder = new StringBuilder(path); 100 | if(path.contains("?")) { 101 | pathBuilder.append("&"); 102 | }else { 103 | pathBuilder.append("?"); 104 | } 105 | Optional.ofNullable(params).orElseGet(HashMap::new).entrySet().forEach(ent->{ 106 | pathBuilder.append(ent.getKey()+"="+ent.getValue()+"&"); 107 | }); 108 | return pathBuilder.toString(); 109 | } 110 | 111 | private String executeHttp(HttpUriRequest request){ 112 | 113 | try(CloseableHttpClient httpClient = HttpClients.createDefault()){ 114 | CloseableHttpResponse execute = httpClient.execute(request); 115 | 116 | if(execute.getStatusLine().getStatusCode() == 200) { 117 | return EntityUtils.toString(execute.getEntity()); 118 | }else { 119 | log.warn(EntityUtils.toString(execute.getEntity())); 120 | } 121 | 122 | } catch (IOException e) { 123 | e.printStackTrace(); 124 | } 125 | 126 | return null; 127 | } 128 | private void buildPostBody(Object body, HttpPost httpPost) { 129 | if(body!= null){ 130 | String jsonBody = JsonUtils.Obj2JsonString(body); 131 | try { 132 | StringEntity entity = new StringEntity(jsonBody); 133 | httpPost.setEntity(entity); 134 | httpPost.addHeader("Content-type", "application/json"); 135 | } catch (UnsupportedEncodingException e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | public class JsonUtils { 8 | static ObjectMapper objectMapper; 9 | static { 10 | objectMapper = new ObjectMapper(); 11 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); 12 | } 13 | 14 | public static String Obj2JsonString(Object obj) { 15 | try { 16 | return objectMapper.writeValueAsString(obj); 17 | } catch (JsonProcessingException e) { 18 | e.printStackTrace(); 19 | } 20 | return ""; 21 | } 22 | 23 | public static ObjectMapper getObjectMapper(){ 24 | return objectMapper; 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/SignUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.constant.WhaleexConstant; 4 | import com.whaleex.api.client.pojo.StringPair; 5 | import com.whaleex.api.client.pojo.Symbol; 6 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 7 | 8 | import java.io.UnsupportedEncodingException; 9 | import java.math.BigDecimal; 10 | import java.net.URLEncoder; 11 | import java.nio.ByteBuffer; 12 | import java.nio.ByteOrder; 13 | import java.util.*; 14 | 15 | public class SignUtils { 16 | 17 | public static String signDateOrder(AutoOrderRequest order, String account, long orderId, Symbol symbol) { 18 | List list = new ArrayList<>(); 19 | String timestamp = System.currentTimeMillis() + ""; 20 | // String timestamp = "0"; 21 | list.add(new StringPair("APIKey", StoreUtils.getStoreAPIkey())); 22 | list.add(new StringPair("pk", StoreUtils.getStorePublicKey())); 23 | list.add(new StringPair("timestamp", timestamp)); 24 | list.add(new StringPair("orderId", orderId + "")); 25 | 26 | String sigParams = getSigParam(list); 27 | String sig = signOrder(orderId, order, timestamp, account, symbol); 28 | 29 | 30 | return sigParams + "&Signature=" + sig; 31 | } 32 | 33 | private static String signOrder(Long orderId, AutoOrderRequest order, String timestamp, String account, Symbol symbol) { 34 | int length = account.length() + WhaleexConstant.EX_EOSACCOUNT.length() + 3 * 8 + 4 + 4 + 35 | symbol.getBaseContract().length() + symbol.getBaseCurrency().length() + symbol.getQuoteContract().length() + 36 | symbol.getQuoteCurrency().length(); 37 | ByteBuffer buffer = ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN); 38 | buffer.put(account.getBytes()); 39 | buffer.put(WhaleexConstant.EX_EOSACCOUNT.getBytes()); 40 | buffer.putLong(orderId); 41 | buffer.putInt(Integer.valueOf((Long.valueOf(timestamp) / 1000) + "")); 42 | if (AutoOrderRequest.OrderType.buy_limit.equals(order.getType())) { 43 | buffer.put(symbol.getQuoteContract().getBytes()); 44 | buffer.put(symbol.getQuoteCurrency().getBytes()); 45 | buffer.putLong(multiply(order.getPrice(), order.getAmount(), symbol.getQuotePrecision(), true)); 46 | buffer.put(symbol.getBaseContract().getBytes()); 47 | buffer.put(symbol.getBaseCurrency().getBytes()); 48 | buffer.putLong(multiply("1", order.getAmount(), symbol.getBasePrecision(), false)); 49 | } 50 | if (AutoOrderRequest.OrderType.sell_limit.equals(order.getType())) { 51 | buffer.put(symbol.getBaseContract().getBytes()); 52 | buffer.put(symbol.getBaseCurrency().getBytes()); 53 | buffer.putLong(multiply("1", order.getAmount(), symbol.getBasePrecision(), false)); 54 | buffer.put(symbol.getQuoteContract().getBytes()); 55 | buffer.put(symbol.getQuoteCurrency().getBytes()); 56 | buffer.putLong(multiply(order.getPrice(), order.getAmount(), symbol.getQuotePrecision(), false)); 57 | } 58 | if (AutoOrderRequest.OrderType.buy_market.equals(order.getType())) { 59 | buffer.put(symbol.getQuoteContract().getBytes()); 60 | buffer.put(symbol.getQuoteCurrency().getBytes()); 61 | buffer.putLong(multiply("1", order.getAmount(), symbol.getQuotePrecision(), false)); 62 | buffer.put(symbol.getBaseContract().getBytes()); 63 | buffer.put(symbol.getBaseCurrency().getBytes()); 64 | buffer.putLong(0l); 65 | } 66 | if (AutoOrderRequest.OrderType.sell_market.equals(order.getType())) { 67 | buffer.put(symbol.getBaseContract().getBytes()); 68 | buffer.put(symbol.getBaseCurrency().getBytes()); 69 | buffer.putLong(multiply("1",order.getAmount(), symbol.getBasePrecision(), false)); 70 | buffer.put(symbol.getQuoteContract().getBytes()); 71 | buffer.put(symbol.getQuoteCurrency().getBytes()); 72 | buffer.putLong(0l); 73 | } 74 | buffer.putShort(new Short("10").shortValue()); 75 | buffer.putShort(new Short("10").shortValue()); 76 | byte[] array = buffer.array(); 77 | System.out.println(array); 78 | buffer.clear(); 79 | 80 | // String hashData = HashUtils.sha256String(array); 81 | String sign = Ecc.Sign(array, StoreUtils.getStorePrivateKey()); 82 | 83 | return sign; 84 | 85 | } 86 | 87 | private static long multiply(String m, String n, int precision, boolean ceil) { 88 | BigDecimal res = new BigDecimal(m).multiply(new BigDecimal(n)); 89 | if (ceil) { 90 | return res.movePointRight(precision).setScale(0, BigDecimal.ROUND_UP).longValue(); 91 | } else { 92 | return res.movePointRight(precision).longValue(); 93 | } 94 | } 95 | 96 | 97 | private static String getSigParam(List sigParam) { 98 | StringBuilder param = new StringBuilder(); 99 | sigParam.forEach(par -> { 100 | param.append(par.getFirst() + "=" + par.getSecond() + "&"); 101 | }); 102 | return param.deleteCharAt(param.length() - 1).toString(); 103 | } 104 | 105 | 106 | public static String signParam(String uri, String method, Map params) throws UnsupportedEncodingException { 107 | List list = new LinkedList<>(); 108 | Optional.ofNullable(params).orElseGet(HashMap::new).entrySet().forEach(ent -> { 109 | list.add(new StringPair(ent.getKey(), ent.getValue())); 110 | }); 111 | Collections.sort(list, Comparator.comparing(StringPair::getFirst)); 112 | String paramString = getSigParam(list); 113 | 114 | String signData = method.toUpperCase() + "\n" + WhaleexConstant.API_BASE_URL + "\n" + uri + "\n" + URLEncoder.encode(paramString, "UTF-8"); 115 | String signature = Ecc.Sign(signData, StoreUtils.getStorePrivateKey()); 116 | 117 | return paramString + "&Signature=" + signature; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/StoreUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import java.io.*; 4 | import java.util.Properties; 5 | 6 | /** 7 | * 本地储存的相关utils 8 | */ 9 | public class StoreUtils { 10 | static Properties properties = new Properties(); 11 | static { 12 | try { 13 | InputStream fis = StoreUtils.class.getClassLoader().getResourceAsStream("keys.properties"); 14 | properties.load(fis); 15 | } catch (FileNotFoundException e) { 16 | e.printStackTrace(); 17 | } catch (IOException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | private static void storeProperties(String key,String value){ 22 | File file = new File(StoreUtils.class.getClassLoader().getResource("keys.properties").getFile()); 23 | try { 24 | FileOutputStream outStream = new FileOutputStream(file); 25 | properties.setProperty(key, value); 26 | properties.store(outStream,null); 27 | 28 | } catch (FileNotFoundException e) { 29 | e.printStackTrace(); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | } 33 | 34 | 35 | } 36 | 37 | public static String getStorePublicKey(){ 38 | return properties.getProperty("publicKey"); 39 | } 40 | 41 | public static void storePublicKye(String publicKey){ 42 | storeProperties("publicKey",publicKey); 43 | } 44 | public static String getStorePrivateKey(){ 45 | return properties.getProperty("privateKey"); 46 | } 47 | 48 | public static void storePrivateKye(String privateKey){ 49 | storeProperties("privateKey",privateKey); 50 | } 51 | 52 | public static String getStoreAPIkey(){ 53 | return properties.getProperty("apiKey"); 54 | } 55 | public static void storeAPIKey(String apiKey){ 56 | storeProperties("apiKey",apiKey); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/Hmac.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.whaleex.api.client.util.crypto; 18 | 19 | 20 | import com.whaleex.api.client.util.crypto.util.BitUtils; 21 | import com.whaleex.api.client.util.crypto.util.HexUtils; 22 | 23 | import java.security.MessageDigest; 24 | import java.security.NoSuchAlgorithmException; 25 | 26 | 27 | public class Hmac { 28 | 29 | private static final String SHA256 = "SHA-256"; 30 | private static final String SHA512 = "SHA-512"; 31 | private static final int SHA256_BLOCK_SIZE = 64; 32 | private static final int SHA512_BLOCK_SIZE = 128; 33 | 34 | public static byte[] hmacSha256(byte[] key, byte[] message) { 35 | MessageDigest digest; 36 | try { 37 | digest = MessageDigest.getInstance(SHA256); 38 | } catch (NoSuchAlgorithmException e) { 39 | // Only happens if the platform does not support SHA-256 40 | throw new RuntimeException(e); 41 | } 42 | return hmac(digest, SHA256_BLOCK_SIZE, key, message); 43 | } 44 | 45 | public static byte[] hmacSha512(byte[] key, byte[] message) { 46 | MessageDigest digest; 47 | try { 48 | digest = MessageDigest.getInstance(SHA512); 49 | } catch (NoSuchAlgorithmException e) { 50 | // Only happens if the platform does not support SHA-512 51 | throw new RuntimeException(e); 52 | } 53 | return hmac(digest, SHA512_BLOCK_SIZE, key, message); 54 | } 55 | 56 | private static byte[] hmac(MessageDigest digest, int blockSize, byte[] key, byte[] message) { 57 | 58 | // Ensure sufficient key length 59 | if (key.length > blockSize) { 60 | key = hash(digest, key); 61 | } 62 | if (key.length < blockSize) { 63 | // Zero pad 64 | byte[] temp = new byte[blockSize]; 65 | System.arraycopy(key, 0, temp, 0, key.length); 66 | key = temp; 67 | } 68 | 69 | // Prepare o key pad 70 | byte[] o_key_pad = new byte[blockSize]; 71 | for (int i = 0; i < blockSize; i++) { 72 | o_key_pad[i] = (byte) (0x5c ^ key[i]); 73 | } 74 | 75 | // Prepare i key pad 76 | byte[] i_key_pad = new byte[blockSize]; 77 | for (int i = 0; i < blockSize; i++) { 78 | i_key_pad[i] = (byte) (0x36 ^ key[i]); 79 | } 80 | 81 | return hash(digest, o_key_pad, hash(digest, i_key_pad, message)); 82 | } 83 | 84 | private static byte[] hash(MessageDigest digest, byte[] data) { 85 | digest.reset(); 86 | digest.update(data, 0, data.length); 87 | return digest.digest(); 88 | } 89 | 90 | private static byte[] hash(MessageDigest digest, byte[] data1, byte[] data2) { 91 | digest.reset(); 92 | digest.update(data1, 0, data1.length); 93 | digest.update(data2, 0, data2.length); 94 | return digest.digest(); 95 | } 96 | 97 | /** 98 | * Run test vectors from RFC-4231 99 | * 100 | * @return true iff the tests succeed 101 | */ 102 | public static boolean testTestVectors() { 103 | byte[] key, data, expected_256, expected_512, result_256, result_512; 104 | 105 | // Test case 1 106 | key = HexUtils.toBytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); 107 | data = HexUtils.toBytes("4869205468657265"); 108 | expected_256 = HexUtils.toBytes("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"); 109 | expected_512 = HexUtils 110 | .toBytes("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"); 111 | result_256 = Hmac.hmacSha256(key, data); 112 | result_512 = Hmac.hmacSha512(key, data); 113 | if (!BitUtils.areEqual(result_256, expected_256) || !BitUtils.areEqual(result_512, expected_512)) { 114 | return false; 115 | } 116 | 117 | // Test case 2 118 | key = HexUtils.toBytes("4a656665"); 119 | data = HexUtils.toBytes("7768617420646f2079612077616e7420666f72206e6f7468696e673f"); 120 | expected_256 = HexUtils.toBytes("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); 121 | expected_512 = HexUtils 122 | .toBytes("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"); 123 | result_256 = Hmac.hmacSha256(key, data); 124 | result_512 = Hmac.hmacSha512(key, data); 125 | if (!BitUtils.areEqual(result_256, expected_256) || !BitUtils.areEqual(result_512, expected_512)) { 126 | return false; 127 | } 128 | 129 | // Test case 3 130 | key = HexUtils.toBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 131 | data = HexUtils 132 | .toBytes("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"); 133 | expected_256 = HexUtils.toBytes("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"); 134 | expected_512 = HexUtils 135 | .toBytes("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"); 136 | result_256 = Hmac.hmacSha256(key, data); 137 | result_512 = Hmac.hmacSha512(key, data); 138 | return !(!BitUtils.areEqual(result_256, expected_256) || !BitUtils.areEqual(result_512, expected_512)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/digest/GeneralDigest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.whaleex.api.client.util.crypto.digest; 18 | 19 | /** 20 | * base implementation of MD4 family style digest as outlined in 21 | * "Handbook of Applied Cryptography", pages 344 - 347. 22 | */ 23 | public abstract class GeneralDigest { 24 | private static final int BYTE_LENGTH = 64; 25 | private byte[] xBuf; 26 | private int xBufOff; 27 | 28 | private long byteCount; 29 | 30 | /** 31 | * Standard constructor 32 | */ 33 | protected GeneralDigest() { 34 | xBuf = new byte[4]; 35 | xBufOff = 0; 36 | } 37 | 38 | /** 39 | * Copy constructor. We are using copy constructors in place of the 40 | * Object.clone() interface as this interface is not supported by J2ME. 41 | */ 42 | protected GeneralDigest(GeneralDigest t) { 43 | xBuf = new byte[t.xBuf.length]; 44 | System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); 45 | 46 | xBufOff = t.xBufOff; 47 | byteCount = t.byteCount; 48 | } 49 | 50 | public void update(byte in) { 51 | xBuf[xBufOff++] = in; 52 | 53 | if (xBufOff == xBuf.length) { 54 | processWord(xBuf, 0); 55 | xBufOff = 0; 56 | } 57 | 58 | byteCount++; 59 | } 60 | 61 | public void update(byte[] in, int inOff, int len) { 62 | // 63 | // fill the current word 64 | // 65 | while ((xBufOff != 0) && (len > 0)) { 66 | update(in[inOff]); 67 | 68 | inOff++; 69 | len--; 70 | } 71 | 72 | // 73 | // process whole words. 74 | // 75 | while (len > xBuf.length) { 76 | processWord(in, inOff); 77 | 78 | inOff += xBuf.length; 79 | len -= xBuf.length; 80 | byteCount += xBuf.length; 81 | } 82 | 83 | // 84 | // load in the remainder. 85 | // 86 | while (len > 0) { 87 | update(in[inOff]); 88 | 89 | inOff++; 90 | len--; 91 | } 92 | } 93 | 94 | public void finish() { 95 | long bitLength = (byteCount << 3); 96 | 97 | // 98 | // add the pad bytes. 99 | // 100 | update((byte) 128); 101 | 102 | while (xBufOff != 0) { 103 | update((byte) 0); 104 | } 105 | 106 | processLength(bitLength); 107 | 108 | processBlock(); 109 | } 110 | 111 | public void reset() { 112 | byteCount = 0; 113 | 114 | xBufOff = 0; 115 | for (int i = 0; i < xBuf.length; i++) { 116 | xBuf[i] = 0; 117 | } 118 | } 119 | 120 | public int getByteLength() { 121 | return BYTE_LENGTH; 122 | } 123 | 124 | protected abstract void processWord(byte[] in, int inOff); 125 | 126 | protected abstract void processLength(long bitLength); 127 | 128 | protected abstract void processBlock(); 129 | } 130 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/digest/Sha256.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.crypto.digest; 2 | 3 | 4 | import com.google.common.base.Preconditions; 5 | import com.whaleex.api.client.util.crypto.util.HexUtils; 6 | 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.util.Arrays; 10 | 11 | 12 | /** 13 | * represents the result of a SHA256 hashing operation prefer to use the static 14 | * factory methods. 15 | */ 16 | public class Sha256 { 17 | 18 | public static final int HASH_LENGTH = 32; 19 | public static final Sha256 ZERO_HASH = new Sha256(new byte[HASH_LENGTH]); 20 | 21 | final private byte[] mHashBytes; 22 | 23 | /** 24 | * create Sha256 from raw hash bytes. 25 | * @param bytes 26 | */ 27 | public Sha256(byte[] bytes) { 28 | Preconditions.checkArgument(bytes.length == HASH_LENGTH); 29 | this.mHashBytes = bytes; 30 | } 31 | 32 | private static MessageDigest getSha256Digest() { 33 | try { 34 | return MessageDigest.getInstance( "SHA-256" ); 35 | } catch (NoSuchAlgorithmException e) { 36 | throw new RuntimeException(e); //cannot happen 37 | } 38 | } 39 | 40 | public static Sha256 from(byte[] data) { 41 | MessageDigest digest; 42 | digest = getSha256Digest(); 43 | digest.update(data, 0, data.length); 44 | return new Sha256(digest.digest()); 45 | } 46 | 47 | public static Sha256 from(byte[] data, int offset, int length) { 48 | MessageDigest digest; 49 | digest = getSha256Digest(); 50 | digest.update(data, offset, length); 51 | return new Sha256(digest.digest()); 52 | } 53 | 54 | public static Sha256 from(byte[] data1, byte[] data2) { 55 | MessageDigest digest; 56 | digest = getSha256Digest(); 57 | digest.update(data1, 0, data1.length); 58 | digest.update(data2, 0, data2.length); 59 | return new Sha256(digest.digest()); 60 | 61 | } 62 | 63 | public static Sha256 doubleHash(byte[] data, int offset, int length) { 64 | MessageDigest digest; 65 | digest = getSha256Digest(); 66 | digest.update(data, offset, length); 67 | return new Sha256(digest.digest(digest.digest())); 68 | } 69 | 70 | 71 | 72 | @Override 73 | public boolean equals(Object other) { 74 | if (other == this) { 75 | return true; 76 | } 77 | if (!(other instanceof Sha256)) 78 | return false; 79 | return Arrays.equals(mHashBytes, ((Sha256) other).mHashBytes); 80 | } 81 | 82 | 83 | @Override 84 | public String toString() { 85 | return HexUtils.toHex(mHashBytes); 86 | } 87 | 88 | public byte[] getBytes() { 89 | return mHashBytes; 90 | } 91 | 92 | public boolean equalsFromOffset(byte[] toCompareData, int offsetInCompareData, int len ) { 93 | if ( ( null == toCompareData) || ( offsetInCompareData < 0) 94 | || ( len < 0) || ( mHashBytes.length <= len ) 95 | || ( toCompareData.length <= offsetInCompareData) ) { 96 | return false; 97 | } 98 | 99 | for (int i = 0; i < len; i++) { 100 | 101 | if ( mHashBytes[i] != toCompareData[ offsetInCompareData + i] ) { 102 | return false; 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | public int length() { 110 | return HASH_LENGTH; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/digest/Sha512.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.crypto.digest; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.primitives.Ints; 5 | import com.whaleex.api.client.util.crypto.util.HexUtils; 6 | 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.util.Arrays; 10 | 11 | 12 | public class Sha512 implements Comparable { 13 | 14 | public static final int HASH_LENGTH = 64; 15 | public static final Sha512 ZERO_HASH = new Sha512(new byte[HASH_LENGTH]); 16 | 17 | final private byte[] mHashBytes; 18 | 19 | public Sha512(byte[] bytes) { 20 | Preconditions.checkArgument(bytes.length == HASH_LENGTH); 21 | this.mHashBytes = bytes; 22 | } 23 | 24 | public static Sha512 from(byte[] data) { 25 | MessageDigest digest; 26 | try { 27 | digest = MessageDigest.getInstance("SHA-512"); 28 | } catch (NoSuchAlgorithmException e) { 29 | throw new RuntimeException(e); //cannot happen 30 | } 31 | 32 | digest.update(data, 0, data.length); 33 | 34 | return new Sha512(digest.digest()); 35 | } 36 | 37 | 38 | private Sha512(byte[] bytes, int offset) { 39 | //defensive copy, since incoming bytes is of arbitrary length 40 | mHashBytes = new byte[HASH_LENGTH]; 41 | System.arraycopy(bytes, offset, mHashBytes, 0, HASH_LENGTH); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object other) { 46 | if (other == this) { 47 | return true; 48 | } 49 | if (!(other instanceof Sha512)) 50 | return false; 51 | return Arrays.equals(mHashBytes, ((Sha512) other).mHashBytes); 52 | } 53 | 54 | 55 | @Override 56 | public String toString() { 57 | return HexUtils.toHex(mHashBytes); 58 | } 59 | 60 | public byte[] getBytes() { 61 | return mHashBytes; 62 | } 63 | 64 | @Override 65 | public int compareTo(Sha512 o) { 66 | for (int i = 0; i < HASH_LENGTH; i++) { 67 | byte myByte = mHashBytes[i]; 68 | byte otherByte = o.mHashBytes[i]; 69 | 70 | final int compare = Ints.compare(myByte, otherByte); 71 | if (compare != 0) 72 | return compare; 73 | } 74 | return 0; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/CurveParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.whaleex.api.client.util.crypto.ec; 26 | 27 | 28 | import com.whaleex.api.client.util.crypto.util.HexUtils; 29 | 30 | import java.math.BigInteger; 31 | 32 | /** 33 | * Created by swapnibble on 2018-02-02. 34 | */ 35 | 36 | public class CurveParam { 37 | public static final int SECP256_K1 = 0; 38 | public static final int SECP256_R1 = 1; 39 | 40 | private final int curveParamType; 41 | private final EcCurve curve; 42 | private final EcPoint G; 43 | private final BigInteger n; 44 | //private final BigInteger h; 45 | 46 | private final BigInteger HALF_CURVE_ORDER; 47 | 48 | public CurveParam( int curveParamType, String pInHex, String aInHex, String bInHex, String GxInHex, String GyInHex, String nInHex ){ 49 | this.curveParamType = curveParamType; 50 | BigInteger p = new BigInteger(pInHex, 16); //p 51 | BigInteger b = new BigInteger(bInHex , 16); 52 | BigInteger a = new BigInteger( aInHex, 16); 53 | curve = new EcCurve(p, a, b); 54 | 55 | G = curve.decodePoint( HexUtils.toBytes("04" + GxInHex + GyInHex) ); 56 | n = new BigInteger(nInHex, 16); 57 | //h = BigInteger.ONE; 58 | 59 | HALF_CURVE_ORDER = n.shiftRight(1); 60 | } 61 | 62 | public int getCurveParamType() { 63 | return curveParamType; 64 | } 65 | 66 | public boolean isType(int paramType ) { 67 | return curveParamType == paramType; 68 | } 69 | 70 | 71 | public EcPoint G() { 72 | return this.G; 73 | } 74 | 75 | public BigInteger n() { 76 | return this.n; 77 | } 78 | 79 | public BigInteger halfCurveOrder() { 80 | return HALF_CURVE_ORDER; 81 | } 82 | 83 | public EcCurve getCurve() { 84 | return curve; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EcCurve.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * modified by swapnibble from Mithrilcoin.io 19 | * This code was extracted from the Java cryptography library from 20 | * www.bouncycastle.org. The code has been formatted to comply with the rest of 21 | * the formatting in this library. 22 | */ 23 | package com.whaleex.api.client.util.crypto.ec; 24 | 25 | import java.math.BigInteger; 26 | 27 | /** 28 | * An elliptic curve 29 | */ 30 | public class EcCurve { 31 | 32 | private EcFieldElement _a; 33 | private EcFieldElement _b; 34 | private BigInteger _q; 35 | private EcPoint _infinity; 36 | 37 | public EcCurve(BigInteger q, BigInteger a, BigInteger b) { 38 | this._q = q; 39 | this._a = fromBigInteger(a); 40 | this._b = fromBigInteger(b); 41 | this._infinity = new EcPoint(this, null, null); 42 | } 43 | 44 | public EcFieldElement getA() { 45 | return _a; 46 | } 47 | 48 | public EcFieldElement getB() { 49 | return _b; 50 | } 51 | 52 | public BigInteger getQ() { 53 | return _q; 54 | } 55 | 56 | public EcPoint getInfinity() { 57 | return _infinity; 58 | } 59 | 60 | public int getFieldSize() { 61 | return _q.bitLength(); 62 | } 63 | 64 | public EcFieldElement fromBigInteger(BigInteger x) { 65 | return new EcFieldElement(this._q, x); 66 | } 67 | 68 | 69 | public EcPoint decodePoint(byte[] encodedPoint) { 70 | EcPoint p = null; 71 | // Switch on encoding type 72 | switch (encodedPoint[0]) { 73 | case 0x00: 74 | p = getInfinity(); 75 | break; 76 | case 0x02: 77 | case 0x03: 78 | int ytilde = encodedPoint[0] & 1; 79 | byte[] i = new byte[encodedPoint.length - 1]; 80 | System.arraycopy(encodedPoint, 1, i, 0, i.length); 81 | EcFieldElement x = new EcFieldElement(this._q, new BigInteger(1, i)); 82 | EcFieldElement alpha = x.multiply(x.square().add(_a)).add(_b); 83 | EcFieldElement beta = alpha.sqrt(); 84 | if (beta == null) { 85 | throw new RuntimeException("Invalid compression"); 86 | } 87 | int bit0 = (beta.toBigInteger().testBit(0) ? 1 : 0); 88 | if (bit0 == ytilde) { 89 | p = new EcPoint(this, x, beta, true); 90 | } else { 91 | p = new EcPoint(this, x, new EcFieldElement(this._q, _q.subtract(beta.toBigInteger())), true); 92 | } 93 | break; 94 | case 0x04: 95 | case 0x06: 96 | case 0x07: 97 | byte[] xEnc = new byte[(encodedPoint.length - 1) / 2]; 98 | byte[] yEnc = new byte[(encodedPoint.length - 1) / 2]; 99 | System.arraycopy(encodedPoint, 1, xEnc, 0, xEnc.length); 100 | System.arraycopy(encodedPoint, xEnc.length + 1, yEnc, 0, yEnc.length); 101 | p = new EcPoint(this, new EcFieldElement(this._q, new BigInteger(1, xEnc)), new EcFieldElement(this._q, 102 | new BigInteger(1, yEnc))); 103 | break; 104 | default: 105 | throw new RuntimeException("Invalid encoding 0x" + Integer.toString(encodedPoint[0], 16)); 106 | } 107 | return p; 108 | } 109 | 110 | @Override 111 | public boolean equals(Object obj) { 112 | if (obj == this) { 113 | return true; 114 | } 115 | if (!(obj instanceof EcCurve)) { 116 | return false; 117 | } 118 | EcCurve other = (EcCurve) obj; 119 | return this._q.equals(other._q) && _a.equals(other._a) && _b.equals(other._b); 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | return _a.hashCode() ^ _b.hashCode() ^ _q.hashCode(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EcFieldElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This code was extracted from the Java cryptography library from 19 | * www.bouncycastle.org. The code has been formatted to comply with the rest of 20 | * the formatting in this library. 21 | */ 22 | package com.whaleex.api.client.util.crypto.ec; 23 | 24 | import java.math.BigInteger; 25 | import java.util.Random; 26 | 27 | /** 28 | * This class represents an elliptic field element. 29 | */ 30 | public class EcFieldElement { 31 | 32 | private static final BigInteger TWO = BigInteger.valueOf(2); 33 | 34 | private BigInteger _x; 35 | private BigInteger _q; 36 | 37 | public EcFieldElement(BigInteger q, BigInteger x) { 38 | this._x = x; 39 | if (x.compareTo(q) >= 0) { 40 | throw new IllegalArgumentException("x value too large in field element"); 41 | } 42 | this._q = q; 43 | } 44 | 45 | public BigInteger toBigInteger() { 46 | return _x; 47 | } 48 | 49 | public int getFieldSize() { 50 | return _q.bitLength(); 51 | } 52 | 53 | public BigInteger getQ() { 54 | return _q; 55 | } 56 | 57 | public EcFieldElement add(EcFieldElement b) { 58 | return new EcFieldElement(_q, _x.add(b.toBigInteger()).mod(_q)); 59 | } 60 | 61 | public EcFieldElement subtract(EcFieldElement b) { 62 | return new EcFieldElement(_q, _x.subtract(b.toBigInteger()).mod(_q)); 63 | } 64 | 65 | public EcFieldElement multiply(EcFieldElement b) { 66 | return new EcFieldElement(_q, _x.multiply(b.toBigInteger()).mod(_q)); 67 | } 68 | 69 | // TODO: optimize. much time is spent here. (13% total) 70 | public EcFieldElement divide(EcFieldElement b) { 71 | return new EcFieldElement(_q, _x.multiply(b.toBigInteger().modInverse(_q)).mod(_q)); 72 | } 73 | 74 | public EcFieldElement negate() { 75 | return new EcFieldElement(_q, _x.negate().mod(_q)); 76 | } 77 | 78 | public EcFieldElement square() { 79 | return new EcFieldElement(_q, _x.multiply(_x).mod(_q)); 80 | } 81 | 82 | public EcFieldElement invert() { 83 | return new EcFieldElement(_q, _x.modInverse(_q)); 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return this.toBigInteger().toString(16); 89 | } 90 | 91 | public EcFieldElement sqrt() { 92 | if (!_q.testBit(0)) { 93 | throw new RuntimeException("not done yet"); 94 | } 95 | 96 | if (_q.testBit(1)) { 97 | // z = g^(u+1) + p, p = 4u + 3 98 | EcFieldElement z = new EcFieldElement(_q, _x.modPow(_q.shiftRight(2).add(BigInteger.ONE), _q)); 99 | 100 | return z.square().equals(this) ? z : null; 101 | } 102 | 103 | // p mod 4 == 1 104 | BigInteger qMinusOne = _q.subtract(BigInteger.ONE); 105 | 106 | BigInteger legendreExponent = qMinusOne.shiftRight(1); 107 | if (!(_x.modPow(legendreExponent, _q).equals(BigInteger.ONE))) { 108 | return null; 109 | } 110 | 111 | BigInteger u = qMinusOne.shiftRight(2); 112 | BigInteger k = u.shiftLeft(1).add(BigInteger.ONE); 113 | 114 | BigInteger Q = this._x; 115 | BigInteger fourQ = Q.shiftLeft(2).mod(_q); 116 | 117 | BigInteger U, V; 118 | Random rand = new Random(); 119 | do { 120 | BigInteger P; 121 | do { 122 | P = new BigInteger(_q.bitLength(), rand); 123 | } while (P.compareTo(_q) >= 0 124 | || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, _q).equals(qMinusOne))); 125 | 126 | BigInteger[] result = lucasSequence(_q, P, Q, k); 127 | U = result[0]; 128 | V = result[1]; 129 | 130 | if (V.multiply(V).mod(_q).equals(fourQ)) { 131 | // Integer division by 2, mod q 132 | if (V.testBit(0)) { 133 | V = V.add(_q); 134 | } 135 | 136 | V = V.shiftRight(1); 137 | 138 | return new EcFieldElement(_q, V); 139 | } 140 | } while (U.equals(BigInteger.ONE) || U.equals(qMinusOne)); 141 | 142 | return null; 143 | 144 | } 145 | 146 | private static BigInteger[] lucasSequence(BigInteger p, BigInteger P, BigInteger Q, BigInteger k) { 147 | int n = k.bitLength(); 148 | int s = k.getLowestSetBit(); 149 | 150 | BigInteger Uh = BigInteger.ONE; 151 | BigInteger Vl = TWO; 152 | BigInteger Vh = P; 153 | BigInteger Ql = BigInteger.ONE; 154 | BigInteger Qh = BigInteger.ONE; 155 | 156 | for (int j = n - 1; j >= s + 1; --j) { 157 | Ql = Ql.multiply(Qh).mod(p); 158 | 159 | if (k.testBit(j)) { 160 | Qh = Ql.multiply(Q).mod(p); 161 | Uh = Uh.multiply(Vh).mod(p); 162 | Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); 163 | Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p); 164 | } else { 165 | Qh = Ql; 166 | Uh = Uh.multiply(Vl).subtract(Ql).mod(p); 167 | Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); 168 | Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); 169 | } 170 | } 171 | 172 | Ql = Ql.multiply(Qh).mod(p); 173 | Qh = Ql.multiply(Q).mod(p); 174 | Uh = Uh.multiply(Vl).subtract(Ql).mod(p); 175 | Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); 176 | Ql = Ql.multiply(Qh).mod(p); 177 | 178 | for (int j = 1; j <= s; ++j) { 179 | Uh = Uh.multiply(Vl).mod(p); 180 | Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); 181 | Ql = Ql.multiply(Ql).mod(p); 182 | } 183 | 184 | return new BigInteger[] { Uh, Vl }; 185 | } 186 | 187 | public boolean equals(Object other) { 188 | if (other == this) { 189 | return true; 190 | } 191 | 192 | if (!(other instanceof EcFieldElement)) { 193 | return false; 194 | } 195 | 196 | EcFieldElement o = (EcFieldElement) other; 197 | return _q.equals(o._q) && _x.equals(o._x); 198 | } 199 | 200 | public int hashCode() { 201 | return _q.hashCode() ^ _x.hashCode(); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EcPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This code was extracted from the Java cryptography library from 19 | * www.bouncycastle.org. The code has been formatted to comply with the rest of 20 | * the formatting in this library. 21 | */ 22 | package com.whaleex.api.client.util.crypto.ec; 23 | 24 | import java.math.BigInteger; 25 | 26 | /** 27 | * This class represents an elliptic curve point. 28 | */ 29 | public class EcPoint { 30 | 31 | private EcCurve _curve; 32 | private EcFieldElement _x; 33 | private EcFieldElement _y; 34 | private boolean _compressed; 35 | 36 | public EcPoint(EcCurve curve, EcFieldElement x, EcFieldElement y) { 37 | this(curve, x, y, false); 38 | } 39 | 40 | public EcPoint(EcCurve curve, EcFieldElement x, EcFieldElement y, boolean compressed) { 41 | this._curve = curve; 42 | this._x = x; 43 | this._y = y; 44 | this._compressed = compressed; 45 | } 46 | 47 | public EcCurve getCurve() { 48 | return _curve; 49 | } 50 | 51 | public EcFieldElement getX() { 52 | return _x; 53 | } 54 | 55 | public EcFieldElement getY() { 56 | return _y; 57 | } 58 | 59 | public boolean isInfinity() { 60 | return _x == null && _y == null; 61 | } 62 | 63 | public boolean isCompressed() { 64 | return _compressed; 65 | } 66 | 67 | /** 68 | * return the field element encoded with point compression. (S 4.3.6) 69 | */ 70 | public byte[] getEncoded() { 71 | if (this.isInfinity()) { 72 | return new byte[1]; 73 | } 74 | 75 | int length = EcTools.getByteLength(_x.getFieldSize()); 76 | 77 | if (_compressed) { 78 | byte PC; 79 | 80 | if (this.getY().toBigInteger().testBit(0)) { 81 | PC = 0x03; 82 | } else { 83 | PC = 0x02; 84 | } 85 | 86 | byte[] X = EcTools.integerToBytes(this.getX().toBigInteger(), length); 87 | byte[] PO = new byte[X.length + 1]; 88 | 89 | PO[0] = PC; 90 | System.arraycopy(X, 0, PO, 1, X.length); 91 | 92 | return PO; 93 | } else { 94 | byte[] X = EcTools.integerToBytes(this.getX().toBigInteger(), length); 95 | byte[] Y = EcTools.integerToBytes(this.getY().toBigInteger(), length); 96 | byte[] PO = new byte[X.length + Y.length + 1]; 97 | 98 | PO[0] = 0x04; 99 | System.arraycopy(X, 0, PO, 1, X.length); 100 | System.arraycopy(Y, 0, PO, X.length + 1, Y.length); 101 | 102 | return PO; 103 | } 104 | } 105 | 106 | // B.3 pg 62 107 | public EcPoint add(EcPoint b) { 108 | if (this.isInfinity()) { 109 | return b; 110 | } 111 | 112 | if (b.isInfinity()) { 113 | return this; 114 | } 115 | 116 | // Check if b = this or b = -this 117 | if (this._x.equals(b._x)) { 118 | if (this._y.equals(b._y)) { 119 | // this = b, i.e. this must be doubled 120 | return this.twice(); 121 | } 122 | 123 | // this = -b, i.e. the result is the point at infinity 124 | return this._curve.getInfinity(); 125 | } 126 | 127 | EcFieldElement gamma = b._y.subtract(this._y).divide(b._x.subtract(this._x)); 128 | 129 | EcFieldElement x3 = gamma.square().subtract(this._x).subtract(b._x); 130 | EcFieldElement y3 = gamma.multiply(this._x.subtract(x3)).subtract(this._y); 131 | 132 | return new EcPoint(_curve, x3, y3); 133 | } 134 | 135 | // B.3 pg 62 136 | public EcPoint twice() { 137 | if (this.isInfinity()) { 138 | // Twice identity element (point at infinity) is identity 139 | return this; 140 | } 141 | 142 | if (this._y.toBigInteger().signum() == 0) { 143 | // if y1 == 0, then (x1, y1) == (x1, -y1) 144 | // and hence this = -this and thus 2(x1, y1) == infinity 145 | return this._curve.getInfinity(); 146 | } 147 | 148 | EcFieldElement TWO = this._curve.fromBigInteger(BigInteger.valueOf(2)); 149 | EcFieldElement THREE = this._curve.fromBigInteger(BigInteger.valueOf(3)); 150 | EcFieldElement gamma = this._x.square().multiply(THREE).add(_curve.getA()).divide(_y.multiply(TWO)); 151 | 152 | EcFieldElement x3 = gamma.square().subtract(this._x.multiply(TWO)); 153 | EcFieldElement y3 = gamma.multiply(this._x.subtract(x3)).subtract(this._y); 154 | 155 | return new EcPoint(_curve, x3, y3, this._compressed); 156 | } 157 | 158 | // D.3.2 pg 102 (see Note:) 159 | public EcPoint subtract(EcPoint b) { 160 | if (b.isInfinity()) { 161 | return this; 162 | } 163 | 164 | // Add -b 165 | return add(b.negate()); 166 | } 167 | 168 | public EcPoint negate() { 169 | return new EcPoint(_curve, this._x, this._y.negate(), this._compressed); 170 | } 171 | 172 | @Override 173 | public boolean equals(Object other) { 174 | if (other == this) { 175 | return true; 176 | } 177 | 178 | if (!(other instanceof EcPoint)) { 179 | return false; 180 | } 181 | 182 | EcPoint o = (EcPoint) other; 183 | 184 | if (this.isInfinity()) { 185 | return o.isInfinity(); 186 | } 187 | 188 | return _x.equals(o._x) && _y.equals(o._y); 189 | } 190 | 191 | @Override 192 | public int hashCode() { 193 | if (this.isInfinity()) { 194 | return 0; 195 | } 196 | 197 | return _x.hashCode() ^ _y.hashCode(); 198 | } 199 | 200 | public EcPoint multiply(BigInteger n) { 201 | return EcTools.multiply(this, n); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EcSignature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package com.whaleex.api.client.util.crypto.ec; 25 | 26 | 27 | import com.whaleex.api.client.util.crypto.util.HexUtils; 28 | import org.apache.commons.lang.StringUtils; 29 | 30 | import java.math.BigInteger; 31 | import java.util.Arrays; 32 | 33 | /** 34 | * Created by swapnibble on 2017-09-20. 35 | */ 36 | 37 | public class EcSignature { 38 | private static final String PREFIX = "SIG"; 39 | 40 | public int recId = -1; 41 | 42 | public final BigInteger r; 43 | public final BigInteger s; 44 | public final CurveParam curveParam; 45 | 46 | public EcSignature(BigInteger r, BigInteger s, CurveParam curveParam) { 47 | this.r = r; 48 | this.s = s; 49 | this.curveParam = curveParam; 50 | } 51 | 52 | public EcSignature(BigInteger r, BigInteger s, CurveParam curveParam, int recId) { 53 | this(r, s,curveParam); 54 | 55 | setRecid( recId ); 56 | } 57 | 58 | public EcSignature( String base58Str ){ 59 | String[] parts = EosEcUtil.safeSplitEosCryptoString( base58Str ); 60 | if ( parts.length < 3 ) { 61 | throw new IllegalArgumentException("Invalid private key format: " + base58Str); 62 | } 63 | 64 | if ( PREFIX.equals( parts[0]) == false ) { 65 | throw new IllegalArgumentException("Signature Key has invalid prefix: " + base58Str); 66 | } 67 | 68 | if (StringUtils.isEmpty( parts[2])) { 69 | throw new IllegalArgumentException("Signature has no data: " + base58Str); 70 | } 71 | 72 | this.curveParam = EosEcUtil.getCurveParamFrom( parts[1]); 73 | byte[] rawBytes = EosEcUtil.getBytesIfMatchedRipemd160( parts[2], parts[1], null); 74 | 75 | if ( null == rawBytes ) { 76 | // TODO handle error! 77 | } 78 | 79 | setRecid( rawBytes[0] - 27 - 4 ); // recId encoding 이 recId + 27 + (compressed ? 4 : 0) 이므로 80 | 81 | this.r = new BigInteger(Arrays.copyOfRange(rawBytes, 1, 33) ); 82 | this.s = new BigInteger(Arrays.copyOfRange(rawBytes, 33, 65) ); 83 | } 84 | 85 | public void setRecid(int recid ) { 86 | this.recId = recid; 87 | } 88 | 89 | 90 | /** 91 | * Returns true if the S component is "low", that means it is below HALF_CURVE_ORDER. See BIP62. 93 | */ 94 | 95 | public boolean isCanonical(){ 96 | return s.compareTo( curveParam.halfCurveOrder()) <= 0 ; // Secp256k1Param.HALF_CURVE_ORDER) <= 0; 97 | } 98 | 99 | /** 100 | * Will automatically adjust the S component to be less than or equal to half the curve order, if necessary. 101 | * This is required because for every signature (r,s) the signature (r, -s (mod N)) is a valid signature of 102 | * the same message. However, we dislike the ability to modify the bits of a Bitcoin transaction after it's 103 | * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be 104 | * considered legal and the other will be banned. 105 | */ 106 | public EcSignature toCanonicalised() { 107 | if (!isCanonical()) { 108 | // The order of the curve is the number of valid points that exist on that curve. If S is in the upper 109 | // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that 110 | // N = 10 111 | // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. 112 | // 10 - 8 == 2, giving us always the latter solution, which is canonical. 113 | return new EcSignature(r, curveParam.n().subtract(s), curveParam); //Secp256k1Param.n.subtract(s)); 114 | } else { 115 | return this; 116 | } 117 | } 118 | 119 | @Override 120 | public boolean equals( Object other) { 121 | if (this == other) 122 | return true; 123 | 124 | if ( null == other || getClass() != other.getClass()) 125 | return false; 126 | 127 | EcSignature otherSig = (EcSignature) other; 128 | return r.equals(otherSig.r) && s.equals(otherSig.s); 129 | } 130 | 131 | public boolean isRSEachLength(int length) { 132 | return (r.toByteArray().length == length) && ( s.toByteArray().length == length) ; 133 | } 134 | 135 | 136 | public String eosEncodingHex( boolean compressed ) { 137 | if ( recId < 0 || recId > 3) { 138 | throw new IllegalStateException("signature has invalid recid."); 139 | } 140 | 141 | int headerByte = recId + 27 + (compressed ? 4 : 0); 142 | byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S 143 | sigData[0] = (byte) headerByte; 144 | System.arraycopy(EcTools.integerToBytes( this.r, 32), 0, sigData, 1, 32); 145 | System.arraycopy(EcTools.integerToBytes( this.s, 32), 0, sigData, 33, 32); 146 | 147 | return EosEcUtil.encodeEosCrypto( PREFIX, curveParam , sigData); 148 | } 149 | 150 | @Override 151 | public String toString(){ 152 | if ( recId < 0 || recId > 3) { 153 | return "no recovery sig: "+ HexUtils.toHex(this.r.toByteArray()) + HexUtils.toHex(this.s.toByteArray()); 154 | } 155 | 156 | return eosEncodingHex( true ); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EcTools.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Parts of this code was extracted from the Java cryptography library from 19 | * www.bouncycastle.org. 20 | */ 21 | package com.whaleex.api.client.util.crypto.ec; 22 | 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * modified by swapnibble from Mithrilcoin.io 27 | * Various tools for elliptic curves 28 | */ 29 | public class EcTools { 30 | 31 | private static CurveParam[] sCurveParams = new CurveParam[2]; 32 | 33 | public static CurveParam getCurveParam( int curveType ){ 34 | 35 | if ( (curveType < 0 ) || ( sCurveParams.length <= curveType ) ) { 36 | throw new IllegalArgumentException("Unknown Curve Type: " + curveType); 37 | } 38 | 39 | if ( null == sCurveParams[curveType] ) { 40 | if (CurveParam.SECP256_K1 == curveType) { 41 | sCurveParams[CurveParam.SECP256_K1] = new CurveParam( CurveParam.SECP256_K1 42 | ,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F" // p 43 | , "0" // a 44 | , "7" // b 45 | , "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" //Gx 46 | , "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" //Gy 47 | , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); // n 48 | } 49 | else if (CurveParam.SECP256_R1 == curveType) { 50 | sCurveParams[CurveParam.SECP256_R1] = new CurveParam( CurveParam.SECP256_R1 51 | , "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff" // p 52 | , "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc" // a 53 | , "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b" // b 54 | , "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" //Gx 55 | , "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" //Gy 56 | , "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); // n 57 | } 58 | } 59 | 60 | return sCurveParams[curveType]; 61 | } 62 | 63 | 64 | /** 65 | * Get the length of the byte encoding of a field element 66 | */ 67 | public static int getByteLength(int fieldSize) { 68 | return (fieldSize + 7) / 8; 69 | } 70 | 71 | /** 72 | * Get a big integer as an array of bytes of a specified length 73 | */ 74 | public static byte[] integerToBytes(BigInteger s, int length) { 75 | byte[] bytes = s.toByteArray(); 76 | 77 | if (length < bytes.length) { 78 | // The length is smaller than the byte representation. Truncate by 79 | // copying over the least significant bytes 80 | byte[] tmp = new byte[length]; 81 | System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length); 82 | return tmp; 83 | } else if (length > bytes.length) { 84 | // The length is larger than the byte representation. Copy over all 85 | // bytes and leave it prefixed by zeros. 86 | byte[] tmp = new byte[length]; 87 | System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length); 88 | return tmp; 89 | } 90 | return bytes; 91 | } 92 | 93 | /** 94 | * Multiply a point with a big integer 95 | */ 96 | public static EcPoint multiply(EcPoint p, BigInteger k) { 97 | BigInteger e = k; 98 | BigInteger h = e.multiply(BigInteger.valueOf(3)); 99 | 100 | EcPoint neg = p.negate(); 101 | EcPoint R = p; 102 | 103 | for (int i = h.bitLength() - 2; i > 0; --i) { 104 | R = R.twice(); 105 | 106 | boolean hBit = h.testBit(i); 107 | boolean eBit = e.testBit(i); 108 | 109 | if (hBit != eBit) { 110 | R = R.add(hBit ? p : neg); 111 | } 112 | } 113 | 114 | return R; 115 | } 116 | 117 | public static EcPoint sumOfTwoMultiplies(EcPoint P, BigInteger k, EcPoint Q, BigInteger l) { 118 | int m = Math.max(k.bitLength(), l.bitLength()); 119 | EcPoint Z = P.add(Q); 120 | EcPoint R = P.getCurve().getInfinity(); 121 | 122 | for (int i = m - 1; i >= 0; --i) { 123 | R = R.twice(); 124 | 125 | if (k.testBit(i)) { 126 | if (l.testBit(i)) { 127 | R = R.add(Z); 128 | } else { 129 | R = R.add(P); 130 | } 131 | } else { 132 | if (l.testBit(i)) { 133 | R = R.add(Q); 134 | } 135 | } 136 | } 137 | 138 | return R; 139 | } 140 | 141 | //ported from BitcoinJ 142 | public static EcPoint decompressKey(CurveParam param, BigInteger x, boolean firstBit) { 143 | int size = 1 + getByteLength( param.getCurve().getFieldSize());// Secp256k1Param.curve.getFieldSize()); 144 | byte[] dest = integerToBytes(x, size); 145 | dest[0] = (byte) (firstBit ? 0x03 : 0x02); 146 | return param.getCurve().decodePoint(dest);// Secp256k1Param.curve.decodePoint(dest); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EosEcUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package com.whaleex.api.client.util.crypto.ec; 25 | 26 | 27 | import com.whaleex.api.client.util.crypto.digest.Ripemd160; 28 | import com.whaleex.api.client.util.crypto.digest.Sha256; 29 | import com.whaleex.api.client.util.crypto.util.Base58; 30 | import com.whaleex.api.client.util.crypto.util.BitUtils; 31 | import com.whaleex.api.client.util.crypto.util.RefValue; 32 | import org.apache.commons.lang.StringUtils; 33 | 34 | import java.util.Arrays; 35 | import java.util.regex.PatternSyntaxException; 36 | 37 | /** 38 | * Created by swapnibble on 2018-02-02. 39 | */ 40 | 41 | public class EosEcUtil { 42 | 43 | public static final String PREFIX_K1 = "K1"; 44 | public static final String PREFIX_R1 = "R1"; 45 | 46 | // public static byte[] decodeEosCrypto(String base58Data, RefValue curveParamRef, RefValue checksumRef ){ 47 | // 48 | // final byte[] retKeyData; 49 | // 50 | // final String typePrefix; 51 | // if ( base58Data.startsWith( EOS_PREFIX ) ) { 52 | // 53 | // if ( base58Data.startsWith( PREFIX_K1, EOS_PREFIX.length())) { 54 | // typePrefix = PREFIX_K1; 55 | // } 56 | // else 57 | // if ( base58Data.startsWith( PREFIX_R1, EOS_PREFIX.length())) { 58 | // typePrefix = PREFIX_R1; 59 | // } 60 | // else { 61 | // typePrefix = null; 62 | // } 63 | // 64 | // retKeyData = getBytesIfMatchedRipemd160( base58Data.substring( EOS_PREFIX.length() ), typePrefix, checksumRef); 65 | // } 66 | // else{ 67 | // typePrefix = null; 68 | // retKeyData = getBytesIfMatchedSha256( base58Data, checksumRef ); 69 | // } 70 | // 71 | // if ( curveParamRef != null) { 72 | // curveParamRef.data = EcTools.getCurveParam( PREFIX_R1.equals( typePrefix ) ? CurveParam.SECP256_R1 : CurveParam.SECP256_K1); 73 | // } 74 | // 75 | // return retKeyData; 76 | // } 77 | 78 | public static byte[] extractFromRipemd160( String base58Data ) { 79 | byte[] data= Base58.decode( base58Data ); 80 | if ( data[0] == data.length) { 81 | return Arrays.copyOfRange(data, 2, data.length ); 82 | } 83 | 84 | return null; 85 | } 86 | 87 | // public static byte[] getBytesIfMatchedRipemd160(String base58Data, String prefix, RefValue checksumRef ){ 88 | // byte[] prefixBytes = StringUtils.isEmpty(prefix) ? new byte[0] : prefix.getBytes(); 89 | // 90 | // byte[] data= Base58.decode( base58Data.substring( prefixBytes.length)); 91 | // 92 | // byte[] toHashData = new byte[data.length - 4 + prefixBytes.length]; 93 | // System.arraycopy( data, 0, toHashData, 0, data.length - 4); // key data 94 | // 95 | // System.arraycopy( prefixBytes, 0, toHashData, data.length - 4, prefixBytes.length); 96 | // 97 | // Ripemd160 ripemd160 = Ripemd160.from( toHashData); //byte[] data, int startOffset, int length 98 | // long checksumByCal = BitUtils.uint32ToLong( ripemd160.bytes(), 0); 99 | // long checksumFromData= BitUtils.uint32ToLong(data, data.length - 4 ); 100 | // if ( checksumByCal != checksumFromData ) { 101 | // throw new IllegalArgumentException("Invalid format, checksum mismatch"); 102 | // } 103 | // 104 | // if ( checksumRef != null ){ 105 | // checksumRef.data = checksumFromData; 106 | // } 107 | // 108 | // return Arrays.copyOfRange(data, 0, data.length - 4); 109 | // } 110 | 111 | public static byte[] getBytesIfMatchedRipemd160(String base58Data, String prefix, RefValue checksumRef ){ 112 | byte[] prefixBytes = StringUtils.isEmpty(prefix) ? new byte[0] : prefix.getBytes(); 113 | 114 | byte[] data= Base58.decode( base58Data ); 115 | 116 | byte[] toHashData = new byte[data.length - 4 + prefixBytes.length]; 117 | System.arraycopy( data, 0, toHashData, 0, data.length - 4); // key data 118 | 119 | System.arraycopy( prefixBytes, 0, toHashData, data.length - 4, prefixBytes.length); 120 | 121 | Ripemd160 ripemd160 = Ripemd160.from( toHashData); //byte[] data, int startOffset, int length 122 | long checksumByCal = BitUtils.uint32ToLong( ripemd160.bytes(), 0); 123 | long checksumFromData= BitUtils.uint32ToLong(data, data.length - 4 ); 124 | if ( checksumByCal != checksumFromData ) { 125 | throw new IllegalArgumentException("Invalid format, checksum mismatch"); 126 | } 127 | 128 | if ( checksumRef != null ){ 129 | checksumRef.data = checksumFromData; 130 | } 131 | 132 | return Arrays.copyOfRange(data, 0, data.length - 4); 133 | } 134 | 135 | public static byte[] getBytesIfMatchedSha256(String base58Data,RefValue checksumRef ){ 136 | byte[] data= Base58.decode( base58Data ); 137 | 138 | // offset 0은 제외, 뒤의 4바이트 제외하고, private key 를 뽑자 139 | Sha256 checkOne = Sha256.from( data, 0, data.length - 4 ); 140 | Sha256 checkTwo = Sha256.from( checkOne.getBytes() ); 141 | if ( checkTwo.equalsFromOffset( data, data.length - 4, 4) 142 | || checkOne.equalsFromOffset( data, data.length - 4, 4) ){ 143 | 144 | if ( checksumRef != null ){ 145 | checksumRef.data = BitUtils.uint32ToLong( data, data.length - 4); 146 | } 147 | 148 | return Arrays.copyOfRange(data, 1, data.length - 4); 149 | }else { 150 | throw new IllegalArgumentException("Invalid format, checksum mismatch"); 151 | } 152 | } 153 | 154 | // public static String encodeEosCrypto(byte[] data, CurveParam curveParam ) { 155 | // boolean isR1 = ( null != curveParam ) && curveParam.isType( CurveParam.SECP256_R1); 156 | // 157 | // byte[] toHashData = new byte[ data.length + (isR1 ? PREFIX_R1.length() : 0) ]; 158 | // System.arraycopy( data, 0, toHashData, 0, data.length); 159 | // if ( isR1 ) { 160 | // System.arraycopy( PREFIX_R1.getBytes(), 0, toHashData, data.length, PREFIX_R1.length()); 161 | // } 162 | // 163 | // byte[] result = new byte[ data.length + 4 ]; 164 | // 165 | // Ripemd160 ripemd160 = Ripemd160.from( toHashData); //byte[] data, int startOffset, int length 166 | // byte[] checksumBytes = ripemd160.bytes(); 167 | // 168 | // System.arraycopy( data, 0, result, 0, data.length); // copy source data 169 | // System.arraycopy( checksumBytes, 0, result, data.length, 4); // copy checksum data 170 | // 171 | // return EOS_PREFIX + ( isR1 ? PREFIX_R1 : "") + Base58.encode( result ); 172 | // } 173 | 174 | public static String encodeEosCrypto(String prefix, CurveParam curveParam, byte[] data ) { 175 | String typePart = ""; 176 | if ( curveParam != null ) { 177 | if ( curveParam.isType( CurveParam.SECP256_K1)) { 178 | typePart = PREFIX_K1; 179 | } 180 | else 181 | if ( curveParam.isType( CurveParam.SECP256_R1)){ 182 | typePart = PREFIX_R1; 183 | } 184 | } 185 | 186 | byte[] toHashData = new byte[ data.length + typePart.length() ]; 187 | System.arraycopy( data, 0, toHashData, 0, data.length); 188 | if ( typePart.length() > 0 ) { 189 | System.arraycopy( typePart.getBytes(), 0, toHashData, data.length, typePart.length()); 190 | } 191 | 192 | byte[] dataToEncodeBase58 = new byte[ data.length + 4 ]; 193 | 194 | Ripemd160 ripemd160 = Ripemd160.from( toHashData); 195 | byte[] checksumBytes = ripemd160.bytes(); 196 | 197 | System.arraycopy( data, 0, dataToEncodeBase58, 0, data.length); // copy source data 198 | System.arraycopy( checksumBytes, 0, dataToEncodeBase58, data.length, 4); // copy checksum data 199 | 200 | 201 | String result; 202 | if ( StringUtils.isEmpty( typePart)) { 203 | result = prefix; 204 | } 205 | else { 206 | result = prefix + EOS_CRYPTO_STR_SPLITTER + typePart + EOS_CRYPTO_STR_SPLITTER; 207 | } 208 | 209 | return result + Base58.encode( dataToEncodeBase58 ); 210 | } 211 | 212 | 213 | 214 | 215 | private static final String EOS_CRYPTO_STR_SPLITTER = "_"; 216 | public static String[] safeSplitEosCryptoString( String cryptoStr ) { 217 | if ( StringUtils.isEmpty( cryptoStr)) { 218 | return new String[]{ cryptoStr }; 219 | } 220 | 221 | try { 222 | return cryptoStr.split( EOS_CRYPTO_STR_SPLITTER ); 223 | } 224 | catch (PatternSyntaxException e){ 225 | e.printStackTrace(); 226 | return new String[]{ cryptoStr }; 227 | } 228 | } 229 | 230 | public static String concatEosCryptoStr( String... strData ) { 231 | 232 | String result=""; 233 | 234 | for ( int i = 0; i < strData.length; i++) { 235 | result += strData[i] + ( i < strData.length -1 ? EOS_CRYPTO_STR_SPLITTER : ""); 236 | } 237 | return result; 238 | } 239 | 240 | public static CurveParam getCurveParamFrom( String curveType ) { 241 | return EcTools.getCurveParam( PREFIX_R1.equals( curveType ) ? CurveParam.SECP256_R1 : CurveParam.SECP256_K1); 242 | } 243 | 244 | 245 | // public static EosCryptoProperty getEosCryptoProperty( String cryptoStr ) { 246 | // if ( StringUtils.isEmpty( cryptoStr)) { 247 | // return new EosCryptoProperty( cryptoStr ); 248 | // } 249 | // 250 | // String[] splitted = null; 251 | // try { 252 | // splitted = cryptoStr.split(EOS_CRYPTO_STR_SPLITTER); 253 | // 254 | // if ( splitted == null || splitted.length <= 1) { 255 | // return new EosCryptoProperty( cryptoStr ); 256 | // } 257 | // 258 | // return new EosCryptoProperty( splitted[0], null, splitted[1]); 259 | // } 260 | // } 261 | } 262 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EosPrivateKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.whaleex.api.client.util.crypto.ec; 26 | 27 | 28 | 29 | import com.whaleex.api.client.util.crypto.digest.Sha256; 30 | import com.whaleex.api.client.util.crypto.util.Base58; 31 | 32 | import java.math.BigInteger; 33 | import java.security.SecureRandom; 34 | 35 | 36 | /** 37 | * Created by swapnibble on 2017-09-25. 38 | * 39 | * 참고: 40 | * https://github.com/EOSIO/eosjs-ecc 41 | * https://github.com/EOSIO/eosjs-ecc/blob/master/src/ecdsa.js 42 | */ 43 | 44 | public class EosPrivateKey { 45 | 46 | private static final String PREFIX = "PVT"; 47 | 48 | private final BigInteger mPrivateKey; 49 | private final EosPublicKey mPublicKey; 50 | 51 | private final CurveParam mCurveParam; 52 | 53 | private static final SecureRandom mSecRandom; 54 | 55 | static { 56 | mSecRandom = new SecureRandom(); 57 | } 58 | 59 | public static SecureRandom getSecuRandom(){ 60 | return mSecRandom; 61 | } 62 | 63 | public EosPrivateKey(){ 64 | this( CurveParam.SECP256_K1); 65 | } 66 | 67 | public EosPrivateKey( int curveParamType){ 68 | mCurveParam = EcTools.getCurveParam(curveParamType); 69 | 70 | mPrivateKey = getOrCreatePrivKeyBigInteger( null ); 71 | mPublicKey = new EosPublicKey(findPubKey( mPrivateKey ), mCurveParam); 72 | } 73 | 74 | public EosPrivateKey( String base58Str ) { 75 | String[] split = EosEcUtil.safeSplitEosCryptoString( base58Str ); 76 | byte[] keyBytes; 77 | 78 | if ( split.length == 1 ){ 79 | mCurveParam = EcTools.getCurveParam( CurveParam.SECP256_K1); 80 | keyBytes = EosEcUtil.getBytesIfMatchedSha256( base58Str, null); 81 | } 82 | else { 83 | if ( split.length < 3 ) { 84 | throw new IllegalArgumentException("Invalid private key format: " + base58Str); 85 | } 86 | 87 | mCurveParam = EosEcUtil.getCurveParamFrom( split[1]); 88 | keyBytes = EosEcUtil.getBytesIfMatchedRipemd160( split[2], split[1], null); 89 | } 90 | 91 | 92 | if ( ( null == keyBytes) || (keyBytes.length < 5 )) { 93 | throw new IllegalArgumentException("Invalid private key length"); 94 | } 95 | 96 | mPrivateKey = getOrCreatePrivKeyBigInteger( keyBytes ); 97 | mPublicKey = new EosPublicKey(findPubKey( mPrivateKey ), mCurveParam); 98 | } 99 | 100 | 101 | public void clear(){ 102 | mPrivateKey.multiply( BigInteger.ZERO ); 103 | } 104 | 105 | private byte[] findPubKey(BigInteger bnum) { 106 | EcPoint Q = EcTools.multiply( mCurveParam.G(), bnum );// Secp256k1Param.G, bnum); 107 | 108 | // Q를 curve 상에서, compressed point 로 변환하자. ( 압축을 위해 ) 109 | 110 | Q = new EcPoint(Q.getCurve(), Q.getX(), Q.getY(), true); 111 | 112 | return Q.getEncoded(); 113 | } 114 | 115 | public EosPublicKey getPublicKey() { 116 | return mPublicKey; 117 | } 118 | 119 | public String toWif() { 120 | byte[] rawPrivKey = getBytes(); 121 | byte[] resultWIFBytes = new byte[ 1 + 32 + 4 ]; 122 | 123 | resultWIFBytes[0] = (byte)0x80; 124 | System.arraycopy( rawPrivKey, rawPrivKey.length > 32 ? 1 : 0, resultWIFBytes, 1 , 32); 125 | 126 | Sha256 hash = Sha256.doubleHash( resultWIFBytes, 0, 33 ); 127 | 128 | System.arraycopy( hash.getBytes(), 0, resultWIFBytes, 33, 4 ); 129 | 130 | return Base58.encode( resultWIFBytes ); 131 | } 132 | 133 | public CurveParam getCurveParam(){ 134 | return mCurveParam; 135 | } 136 | 137 | public EcSignature sign( Sha256 digest ) { 138 | return EcDsa.sign( digest, this); 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | if ( mCurveParam.isType( CurveParam.SECP256_K1 ) ){ 144 | return toWif(); 145 | } 146 | 147 | return EosEcUtil.encodeEosCrypto( PREFIX, mCurveParam , getBytes()); 148 | } 149 | 150 | public BigInteger getAsBigInteger() { 151 | return mPrivateKey; 152 | } 153 | 154 | 155 | public byte[] getBytes() { 156 | byte[] result = new byte[32]; 157 | byte[] bytes = mPrivateKey.toByteArray(); 158 | if (bytes.length <= result.length) { 159 | System.arraycopy(bytes, 0, result, result.length - bytes.length, bytes.length); 160 | } else { 161 | assert bytes.length == 33 && bytes[0] == 0; 162 | System.arraycopy(bytes, 1, result, 0, bytes.length - 1); 163 | } 164 | return result; 165 | } 166 | 167 | public byte[] getBytes(BigInteger value) { 168 | byte[] result = new byte[32]; 169 | byte[] bytes = value.toByteArray(); 170 | if (bytes.length <= result.length) { 171 | System.arraycopy(bytes, 0, result, result.length - bytes.length, bytes.length); 172 | } else { 173 | // This happens if the most significant bit is set and we have an 174 | // extra leading zero to avoid a negative BigInteger 175 | assert bytes.length == 33 && bytes[0] == 0; 176 | System.arraycopy(bytes, 1, result, 0, bytes.length - 1); 177 | } 178 | return result; 179 | } 180 | 181 | private BigInteger toUnsignedBigInteger(BigInteger value ) { 182 | if ( value.signum() < 0 ) { 183 | return new BigInteger( 1, value.toByteArray()); 184 | } 185 | 186 | return value; 187 | } 188 | 189 | private BigInteger toUnsignedBigInteger(byte[] value ) { 190 | if ( (( value[0]) & 0x80) != 0 ) { 191 | return new BigInteger( 1, value); 192 | } 193 | 194 | return new BigInteger(value); 195 | } 196 | 197 | private BigInteger getOrCreatePrivKeyBigInteger(byte[] value ) { 198 | if ( null != value ) { 199 | if (((value[0]) & 0x80) != 0) { 200 | return new BigInteger(1, value); 201 | } 202 | 203 | return new BigInteger(value); 204 | } 205 | 206 | 207 | int nBitLength = mCurveParam.n().bitLength();// Secp256k1Param.n.bitLength(); 208 | BigInteger d; 209 | do { 210 | // Make a BigInteger from bytes to ensure that Android and 'classic' 211 | // java make the same BigIntegers from the same random source with the 212 | // same seed. Using BigInteger(nBitLength, random) 213 | // produces different results on Android compared to 'classic' java. 214 | byte[] bytes = new byte[nBitLength / 8]; 215 | mSecRandom.nextBytes(bytes); 216 | bytes[0] = (byte) (bytes[0] & 0x7F); // ensure positive number 217 | d = new BigInteger(bytes); 218 | } 219 | while (d.equals(BigInteger.ZERO) || (d.compareTo(mCurveParam.n()) >= 0));// Secp256k1Param.n) >= 0)); 220 | 221 | return d; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/ec/EosPublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package com.whaleex.api.client.util.crypto.ec; 25 | 26 | 27 | import com.whaleex.api.client.util.crypto.digest.Ripemd160; 28 | import com.whaleex.api.client.util.crypto.util.BitUtils; 29 | import com.whaleex.api.client.util.crypto.util.RefValue; 30 | 31 | import java.util.Arrays; 32 | 33 | /** 34 | * Created by swapnibble on 2017-09-25. 35 | */ 36 | 37 | public class EosPublicKey { 38 | private static final String LEGACY_PREFIX = "EOS"; 39 | private static final String PREFIX = "PUB"; 40 | 41 | private static final int CHECK_BYTE_LEN = 4; 42 | 43 | private final long mCheck; 44 | private final CurveParam mCurveParam; 45 | private final byte[] mData; 46 | 47 | public static class IllegalEosPubkeyFormatException extends IllegalArgumentException { 48 | public IllegalEosPubkeyFormatException(String pubkeyStr) { 49 | super("invalid eos public key : " + pubkeyStr); 50 | } 51 | } 52 | 53 | public EosPublicKey( byte[] data ){ 54 | this( data, EcTools.getCurveParam( CurveParam.SECP256_K1)); 55 | } 56 | 57 | public EosPublicKey( byte[] data, CurveParam curveParam ){ 58 | mData = Arrays.copyOf(data, 33); 59 | mCurveParam = curveParam; 60 | 61 | mCheck= BitUtils.uint32ToLong( Ripemd160.from( mData, 0, mData.length).bytes(), 0 ); 62 | } 63 | 64 | public EosPublicKey(String base58Str) { 65 | RefValue checksumRef = new RefValue<>(); 66 | 67 | String[] parts = EosEcUtil.safeSplitEosCryptoString( base58Str ); 68 | if ( base58Str.startsWith(LEGACY_PREFIX) ) { 69 | if ( parts.length == 1 ){ 70 | mCurveParam = EcTools.getCurveParam( CurveParam.SECP256_K1); 71 | mData = EosEcUtil.getBytesIfMatchedRipemd160( base58Str.substring( LEGACY_PREFIX.length()), null, checksumRef); 72 | } 73 | else { 74 | throw new IllegalEosPubkeyFormatException( base58Str ); 75 | } 76 | } 77 | else { 78 | if ( parts.length < 3 ) { 79 | throw new IllegalEosPubkeyFormatException( base58Str ); 80 | } 81 | 82 | // [0]: prefix, [1]: curve type, [2]: data 83 | if ( false == PREFIX.equals( parts[0]) ) throw new IllegalEosPubkeyFormatException( base58Str ); 84 | 85 | mCurveParam = EosEcUtil.getCurveParamFrom( parts[1]); 86 | mData = EosEcUtil.getBytesIfMatchedRipemd160( parts[2], parts[1], checksumRef); 87 | } 88 | 89 | mCheck = checksumRef.data; 90 | } 91 | 92 | public byte[] getBytes() { 93 | return mData; 94 | } 95 | 96 | 97 | @Override 98 | public String toString() { 99 | 100 | boolean isR1 = mCurveParam.isType( CurveParam.SECP256_R1 ); 101 | 102 | return EosEcUtil.encodeEosCrypto( isR1 ? PREFIX : LEGACY_PREFIX, isR1 ? mCurveParam : null, mData ); 103 | 104 | // byte[] postfixBytes = isR1 ? EosEcUtil.PREFIX_R1.getBytes() : new byte[0] ; 105 | // byte[] toDigest = new byte[mData.length + postfixBytes.length]; 106 | // System.arraycopy( mData, 0, toDigest, 0, mData.length); 107 | // 108 | // if ( postfixBytes.length > 0) { 109 | // System.arraycopy(postfixBytes, 0, toDigest, mData.length, postfixBytes.length); 110 | // } 111 | // 112 | // byte[] digest = Ripemd160.from( toDigest ).bytes(); 113 | // byte[] result = new byte[ CHECK_BYTE_LEN + mData.length]; 114 | // 115 | // System.arraycopy( mData, 0, result, 0, mData.length); 116 | // System.arraycopy( digest, 0, result, mData.length, CHECK_BYTE_LEN); 117 | // 118 | // if ( isR1 ){ 119 | // return EosEcUtil.concatEosCryptoStr(PREFIX , EosEcUtil.PREFIX_R1, Base58.encode( result ) ); 120 | // } 121 | // else { 122 | // return LEGACY_PREFIX + Base58.encode( result ) ; 123 | // } 124 | } 125 | 126 | @Override 127 | public int hashCode(){ 128 | return (int)(mCheck & 0xFFFFFFFFL ); 129 | } 130 | 131 | @Override 132 | public boolean equals(Object other) { 133 | if ( this == other ) return true; 134 | 135 | if ( null == other || getClass() != other.getClass()) 136 | return false; 137 | 138 | return BitUtils.areEqual( this.mData, ((EosPublicKey)other).mData); 139 | } 140 | 141 | public boolean isCurveParamK1() { 142 | return ( mCurveParam == null || CurveParam.SECP256_K1 == mCurveParam.getCurveParamType() ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/util/Base58.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.crypto.util; 2 | 3 | /** 4 | * Copyright 2011 Google Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.io.UnsupportedEncodingException; 20 | import java.math.BigInteger; 21 | 22 | 23 | /** 24 | *

25 | * Base58 is a way to encode Bitcoin addresses as numbers and letters. Note that 26 | * this is not the same base58 as used by Flickr, which you may see reference to 27 | * around the internet. 28 | *

29 | * 30 | * 31 | *

32 | * Satoshi says: why base-58 instead of standard base-64 encoding? 33 | *

34 | * 35 | *

    36 | *
  • Don't want 0OIl characters that look the same in some fonts and could be 37 | * used to create visually identical looking account numbers.
  • 38 | *
  • A string with non-alphanumeric characters is not as easily accepted as an 39 | * account number.
  • 40 | *
  • E-mail usually won't line-break if there's no punctuation to break at.
  • 41 | *
  • Doubleclicking selects the whole number as one word if it's all 42 | * alphanumeric.
  • 43 | *
44 | */ 45 | public class Base58 { 46 | public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); 47 | 48 | private static final int[] INDEXES = new int[128]; 49 | static { 50 | for (int i = 0; i < INDEXES.length; i++) { 51 | INDEXES[i] = -1; 52 | } 53 | for (int i = 0; i < ALPHABET.length; i++) { 54 | INDEXES[ALPHABET[i]] = i; 55 | } 56 | } 57 | 58 | /** Encodes the given bytes in base58. No checksum is appended. */ 59 | public static String encode(byte[] input) { 60 | if (input.length == 0) { 61 | return ""; 62 | } 63 | input = copyOfRange(input, 0, input.length); 64 | // Count leading zeroes. 65 | int zeroCount = 0; 66 | while (zeroCount < input.length && input[zeroCount] == 0) { 67 | ++zeroCount; 68 | } 69 | // The actual encoding. 70 | byte[] temp = new byte[input.length * 2]; 71 | int j = temp.length; 72 | 73 | int startAt = zeroCount; 74 | while (startAt < input.length) { 75 | byte mod = divmod58(input, startAt); 76 | if (input[startAt] == 0) { 77 | ++startAt; 78 | } 79 | temp[--j] = (byte) ALPHABET[mod]; 80 | } 81 | 82 | // Strip extra '1' if there are some after decoding. 83 | while (j < temp.length && temp[j] == ALPHABET[0]) { 84 | ++j; 85 | } 86 | // Add as many leading '1' as there were leading zeros. 87 | while (--zeroCount >= 0) { 88 | temp[--j] = (byte) ALPHABET[0]; 89 | } 90 | 91 | byte[] output = copyOfRange(temp, j, temp.length); 92 | try { 93 | return new String(output, "US-ASCII"); 94 | } catch (UnsupportedEncodingException e) { 95 | throw new RuntimeException(e); // Cannot happen. 96 | } 97 | } 98 | 99 | /* swapnibble disabled 100 | * Encode an array of bytes as Base58 with an appended checksum as in Bitcoin 101 | * address encoding 102 | 103 | public static String encodeWithChecksum(byte[] input) { 104 | byte[] b = new byte[input.length + 4]; 105 | System.arraycopy(input, 0, b, 0, input.length); 106 | Sha256 checkSum = HashUtils.doubleSha256(b, 0, input.length); 107 | System.arraycopy(checkSum.getBytes(), 0, b, input.length, 4); 108 | return encode(b); 109 | }*/ 110 | 111 | public static byte[] decode(char[] input) { 112 | 113 | if (input.length == 0) { 114 | return new byte[0]; 115 | } 116 | // Get rid of any UTF-8 BOM marker. Those should not be present, but might have slipped in nonetheless, 117 | // since Java does not automatically discard them when reading a stream. Only remove it, if at the beginning 118 | // of the string. Otherwise, something is probably seriously wrong. 119 | int inputLen = input.length; 120 | int startIndex= 0; 121 | if (input[0] == '\uFEFF') { 122 | //input = input.substring(1); 123 | inputLen--; 124 | startIndex = 1; 125 | } 126 | 127 | byte[] input58 = new byte[inputLen]; 128 | // Transform the String to a base58 byte sequence 129 | for (int i = startIndex; i < inputLen; ++i) { 130 | char c = input[i]; 131 | 132 | int digit58 = -1; 133 | if (c >= 0 && c < 128) { 134 | digit58 = INDEXES[c]; 135 | } 136 | if (digit58 < 0) { 137 | return null; 138 | } 139 | 140 | input58[i] = (byte) digit58; 141 | } 142 | // Count leading zeroes 143 | int zeroCount = 0; 144 | while (zeroCount < input58.length && input58[zeroCount] == 0) { 145 | ++zeroCount; 146 | } 147 | // The encoding 148 | byte[] temp = new byte[inputLen]; 149 | int j = temp.length; 150 | 151 | int startAt = zeroCount; 152 | while (startAt < input58.length) { 153 | byte mod = divmod256(input58, startAt); 154 | if (input58[startAt] == 0) { 155 | ++startAt; 156 | } 157 | 158 | temp[--j] = mod; 159 | } 160 | // Do no add extra leading zeroes, move j to first non null byte. 161 | while (j < temp.length && temp[j] == 0) { 162 | ++j; 163 | } 164 | 165 | return copyOfRange(temp, j - zeroCount, temp.length); 166 | } 167 | 168 | public static byte[] decode(String input) { 169 | 170 | if (input.length() == 0) { 171 | return new byte[0]; 172 | } 173 | // Get rid of any UTF-8 BOM marker. Those should not be present, but might have slipped in nonetheless, 174 | // since Java does not automatically discard them when reading a stream. Only remove it, if at the beginning 175 | // of the string. Otherwise, something is probably seriously wrong. 176 | if (input.charAt(0) == '\uFEFF') input = input.substring(1); 177 | 178 | byte[] input58 = new byte[input.length()]; 179 | // Transform the String to a base58 byte sequence 180 | for (int i = 0; i < input.length(); ++i) { 181 | char c = input.charAt(i); 182 | 183 | int digit58 = -1; 184 | if (c >= 0 && c < 128) { 185 | digit58 = INDEXES[c]; 186 | } 187 | if (digit58 < 0) { 188 | return null; 189 | } 190 | 191 | input58[i] = (byte) digit58; 192 | } 193 | // Count leading zeroes 194 | int zeroCount = 0; 195 | while (zeroCount < input58.length && input58[zeroCount] == 0) { 196 | ++zeroCount; 197 | } 198 | // The encoding 199 | byte[] temp = new byte[input.length()]; 200 | int j = temp.length; 201 | 202 | int startAt = zeroCount; 203 | while (startAt < input58.length) { 204 | byte mod = divmod256(input58, startAt); 205 | if (input58[startAt] == 0) { 206 | ++startAt; 207 | } 208 | 209 | temp[--j] = mod; 210 | } 211 | // Do no add extra leading zeroes, move j to first non null byte. 212 | while (j < temp.length && temp[j] == 0) { 213 | ++j; 214 | } 215 | 216 | return copyOfRange(temp, j - zeroCount, temp.length); 217 | } 218 | 219 | 220 | public static BigInteger decodeToBigInteger(String input) { 221 | return new BigInteger(1, decode(input)); 222 | } 223 | 224 | // 225 | // number -> number / 58, returns number % 58 226 | // 227 | private static byte divmod58(byte[] number, int startAt) { 228 | int remainder = 0; 229 | for (int i = startAt; i < number.length; i++) { 230 | int digit256 = (int) number[i] & 0xFF; 231 | int temp = remainder * 256 + digit256; 232 | 233 | number[i] = (byte) (temp / 58); 234 | 235 | remainder = temp % 58; 236 | } 237 | 238 | return (byte) remainder; 239 | } 240 | 241 | // 242 | // number -> number / 256, returns number % 256 243 | // 244 | private static byte divmod256(byte[] number58, int startAt) { 245 | int remainder = 0; 246 | for (int i = startAt; i < number58.length; i++) { 247 | int digit58 = (int) number58[i] & 0xFF; 248 | int temp = remainder * 58 + digit58; 249 | 250 | number58[i] = (byte) (temp / 256); 251 | 252 | remainder = temp % 256; 253 | } 254 | 255 | return (byte) remainder; 256 | } 257 | 258 | private static byte[] copyOfRange(byte[] source, int from, int to) { 259 | byte[] range = new byte[to - from]; 260 | System.arraycopy(source, from, range, 0, range.length); 261 | 262 | return range; 263 | } 264 | } -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/util/BitUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.whaleex.api.client.util.crypto.util; 18 | 19 | import java.io.DataInputStream; 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | 23 | /** 24 | * Utilities for converting between byte arrays and unsigned integer values. 25 | */ 26 | public class BitUtils { 27 | 28 | public static long uint16ToLong(byte[] buf, int offset) { 29 | return ((buf[offset++] & 0xFFL)) | ((buf[offset++] & 0xFFL) << 8); 30 | } 31 | 32 | public static long uint32ToLong(byte[] buf, int offset) { 33 | return ((buf[offset++] & 0xFFL)) | ((buf[offset++] & 0xFFL) << 8) | ((buf[offset++] & 0xFFL) << 16) 34 | | ((buf[offset] & 0xFFL) << 24); 35 | } 36 | 37 | public static long uint64ToLong(byte[] buf, int offset) { 38 | return ((buf[offset++] & 0xFFL)) | ((buf[offset++] & 0xFFL) << 8) | ((buf[offset++] & 0xFFL) << 16) 39 | | ((buf[offset++] & 0xFFL) << 24) | ((buf[offset++] & 0xFFL) << 32) | ((buf[offset++] & 0xFFL) << 40) 40 | | ((buf[offset++] & 0xFFL) << 48) | ((buf[offset++] & 0xFFL) << 56); 41 | } 42 | 43 | public static int uint16FromStream(DataInputStream stream) throws IOException { 44 | return (int) (((stream.read() & 0xFFL)) | ((stream.read() & 0xFFL) << 8)); 45 | } 46 | 47 | public static int uint16FromStreamBE(DataInputStream stream) throws IOException { 48 | return (int) (((stream.read() & 0xFFL) << 8) | ((stream.read() & 0xFFL))); 49 | } 50 | 51 | public static int uint32FromStream(DataInputStream stream) throws IOException { 52 | return (int) (((stream.read() & 0xFFL)) | ((stream.read() & 0xFFL) << 8) | ((stream.read() & 0xFFL) << 16) | ((stream 53 | .read() & 0xFFL) << 24)); 54 | } 55 | 56 | public static long uint64FromStream(DataInputStream stream) throws IOException { 57 | return ((stream.read() & 0xFFL)) | ((stream.read() & 0xFFL) << 8) | ((stream.read() & 0xFFL) << 16) 58 | | ((stream.read() & 0xFFL) << 24) | ((stream.read() & 0xFFL) << 32) | ((stream.read() & 0xFFL) << 40) 59 | | ((stream.read() & 0xFFL) << 48) | ((stream.read() & 0xFFL) << 56); 60 | } 61 | 62 | public static void uint32ToStream(long value, OutputStream stream) throws IOException { 63 | stream.write((byte) (0xFFL & (value))); 64 | stream.write((byte) (0xFFL & (value >> 8))); 65 | stream.write((byte) (0xFFL & (value >> 16))); 66 | stream.write((byte) (0xFFL & (value >> 24))); 67 | } 68 | 69 | public static void uint64ToStream(long value, OutputStream stream) throws IOException { 70 | stream.write((byte) (0xFFL & (value))); 71 | stream.write((byte) (0xFFL & (value >> 8))); 72 | stream.write((byte) (0xFFL & (value >> 16))); 73 | stream.write((byte) (0xFFL & (value >> 24))); 74 | stream.write((byte) (0xFFL & (value >> 32))); 75 | stream.write((byte) (0xFFL & (value >> 40))); 76 | stream.write((byte) (0xFFL & (value >> 48))); 77 | stream.write((byte) (0xFFL & (value >> 56))); 78 | } 79 | 80 | public static void uint32ToByteArrayLE(long value, byte[] output, int offset) { 81 | output[offset] = (byte) (0xFFL & (value)); 82 | output[offset + 1] = (byte) (0xFFL & (value >> 8)); 83 | output[offset + 2] = (byte) (0xFFL & (value >> 16)); 84 | output[offset + 3] = (byte) (0xFFL & (value >> 24)); 85 | } 86 | 87 | public static void uint64ToByteArrayLE(long value, byte[] output, int offset) { 88 | output[offset] = (byte) (0xFFL & (value)); 89 | output[offset + 1] = (byte) (0xFFL & (value >> 8)); 90 | output[offset + 2] = (byte) (0xFFL & (value >> 16)); 91 | output[offset + 3] = (byte) (0xFFL & (value >> 24)); 92 | output[offset + 4] = (byte) (0xFFL & (value >> 32)); 93 | output[offset + 5] = (byte) (0xFFL & (value >> 40)); 94 | output[offset + 6] = (byte) (0xFFL & (value >> 48)); 95 | output[offset + 7] = (byte) (0xFFL & (value >> 56)); 96 | } 97 | 98 | public static boolean areEqual(byte[] a, byte[] b) { 99 | if (a == null && b == null) { 100 | return true; 101 | } 102 | if (a == null || b == null) { 103 | return false; 104 | } 105 | if (a.length != b.length) { 106 | return false; 107 | } 108 | for (int i = 0; i < a.length; i++) { 109 | if (a[i] != b[i]) { 110 | return false; 111 | } 112 | } 113 | return true; 114 | } 115 | 116 | public static byte[] copyByteArray(byte[] source) { 117 | byte[] buf = new byte[source.length]; 118 | System.arraycopy(source, 0, buf, 0, buf.length); 119 | return buf; 120 | } 121 | 122 | /** 123 | * Returns a copy of the given byte array in reverse order. 124 | */ 125 | public static byte[] reverseBytes(byte[] bytes) { 126 | byte[] buf = new byte[bytes.length]; 127 | for (int i = 0; i < bytes.length; i++) 128 | buf[i] = bytes[bytes.length - 1 - i]; 129 | return buf; 130 | } 131 | 132 | /** 133 | * Read a number of bytes or throw. 134 | * 135 | * @param size The number of bytes read 136 | * @return The array of bytes read. 137 | * @throws IOException 138 | */ 139 | public static byte[] readBytes(DataInputStream stream, int size) throws IOException { 140 | byte[] buf = new byte[size]; 141 | int toRead = size; 142 | int done = 0; 143 | while (toRead > 0) { 144 | int read = stream.read(buf, done, toRead); 145 | if (read == -1) { 146 | throw new IOException(); 147 | } 148 | done += read; 149 | toRead -= read; 150 | } 151 | return buf; 152 | } 153 | 154 | // Arrays.copyOf implementation which we can use also on Java versions < 155 | // 1.6 156 | public static byte[] copyOf(byte[] original, int newLength) { 157 | if (newLength < 0) { 158 | throw new IllegalArgumentException(); 159 | } 160 | byte[] buf = new byte[newLength]; 161 | int lastIndex = Math.min(original.length, newLength); 162 | System.arraycopy(original, 0, buf, 0, lastIndex); 163 | return buf; 164 | } 165 | 166 | // Arrays.copyOfRange implementation which we can use also on Java versions < 167 | // 1.6 168 | public static byte[] copyOfRange(byte[] original, int from, int to) { 169 | if (to < from || from < 0 || from > original.length) { 170 | throw new IllegalArgumentException(); 171 | } 172 | byte[] buf = new byte[to - from]; 173 | int lastIndex = Math.min(original.length, to); 174 | System.arraycopy(original, from, buf, 0, lastIndex - from); 175 | return buf; 176 | } 177 | 178 | /** 179 | * Return the concatenation of two byte arrays in a new byte array 180 | */ 181 | public static byte[] concatenate(byte[] a, byte[] b) { 182 | byte[] result = new byte[a.length + b.length]; 183 | System.arraycopy(a, 0, result, 0, a.length); 184 | System.arraycopy(b, 0, result, a.length, b.length); 185 | return result; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/util/CryptUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package com.whaleex.api.client.util.crypto.util; 25 | 26 | import javax.crypto.*; 27 | import javax.crypto.spec.IvParameterSpec; 28 | import javax.crypto.spec.SecretKeySpec; 29 | import java.security.InvalidAlgorithmParameterException; 30 | import java.security.InvalidKeyException; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.util.Arrays; 33 | 34 | 35 | /** 36 | * Created by swapnibble on 2017-08-09. 37 | */ 38 | 39 | public class CryptUtil { 40 | 41 | public static byte[] aesEncrypt( byte[] key, byte[] data, byte[] iv ) { 42 | 43 | byte[] encrypted = null; 44 | 45 | try { 46 | 47 | SecretKey secureKey = new SecretKeySpec(key, "AES"); 48 | Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 49 | 50 | c.init(Cipher.ENCRYPT_MODE, secureKey, new IvParameterSpec(iv)); 51 | 52 | encrypted = c.doFinal(data); 53 | 54 | } catch (NoSuchPaddingException e) { 55 | e.printStackTrace(); 56 | } catch (InvalidAlgorithmParameterException e) { 57 | e.printStackTrace(); 58 | } catch (NoSuchAlgorithmException e) { 59 | e.printStackTrace(); 60 | } catch (InvalidKeyException e) { 61 | e.printStackTrace(); 62 | } catch (BadPaddingException e) { 63 | e.printStackTrace(); 64 | } catch (IllegalBlockSizeException e) { 65 | e.printStackTrace(); 66 | } 67 | 68 | return encrypted; 69 | } 70 | 71 | public static byte[] aesDecrypt( byte[] key, byte[] data, byte[] iv) { 72 | byte[] decrypted = null; 73 | try { 74 | SecretKey secureKey = new SecretKeySpec(key, "AES"); 75 | Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 76 | 77 | if ( iv.length > 16 ) { 78 | iv = Arrays.copyOf( iv, 16 ); // aes/cbc 에선 iv 는 16-byte ! 79 | } 80 | 81 | c.init(Cipher.DECRYPT_MODE, secureKey, new IvParameterSpec(iv)); 82 | 83 | decrypted = c.doFinal(data); 84 | 85 | } catch (NoSuchPaddingException e) { 86 | e.printStackTrace(); 87 | } catch (InvalidAlgorithmParameterException e) { 88 | e.printStackTrace(); 89 | } catch (NoSuchAlgorithmException e) { 90 | e.printStackTrace(); 91 | } catch (InvalidKeyException e) { 92 | e.printStackTrace(); 93 | } catch (BadPaddingException e) { 94 | e.printStackTrace(); 95 | } catch (IllegalBlockSizeException e) { 96 | e.printStackTrace(); 97 | } 98 | 99 | return decrypted; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/util/HexUtils.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.crypto.util; 2 | 3 | /** 4 | * Created by pocketEos on 2018/4/26. 5 | * Utilities for going to and from ASCII-HEX representation. 6 | */ 7 | 8 | public class HexUtils { 9 | 10 | /** 11 | * Encodes an array of bytes as hex symbols. 12 | * 13 | * @param bytes 14 | * the array of bytes to encode 15 | * @return the resulting hex string 16 | */ 17 | public static String toHex(byte[] bytes) { 18 | return toHex(bytes, null); 19 | } 20 | 21 | /** 22 | * Encodes an array of bytes as hex symbols. 23 | * 24 | * @param bytes 25 | * the array of bytes to encode 26 | * @param separator 27 | * the separator to use between two bytes, can be null 28 | * @return the resulting hex string 29 | */ 30 | public static String toHex(byte[] bytes, String separator) { 31 | return toHex(bytes, 0, bytes.length, separator); 32 | } 33 | 34 | /** 35 | * Encodes an array of bytes as hex symbols. 36 | * 37 | * @param bytes 38 | * the array of bytes to encode 39 | * @param offset 40 | * the start offset in the array of bytes 41 | * @param length 42 | * the number of bytes to encode 43 | * @return the resulting hex string 44 | */ 45 | public static String toHex(byte[] bytes, int offset, int length) { 46 | return toHex(bytes, offset, length, null); 47 | } 48 | 49 | /** 50 | * Encodes a single byte to hex symbols. 51 | * 52 | * @param b the byte to encode 53 | * @return the resulting hex string 54 | */ 55 | public static String toHex(byte b) { 56 | StringBuilder sb = new StringBuilder(); 57 | appendByteAsHex(sb, b); 58 | return sb.toString(); 59 | } 60 | 61 | 62 | /** 63 | * Encodes an array of bytes as hex symbols. 64 | * 65 | * @param bytes 66 | * the array of bytes to encode 67 | * @param offset 68 | * the start offset in the array of bytes 69 | * @param length 70 | * the number of bytes to encode 71 | * @param separator 72 | * the separator to use between two bytes, can be null 73 | * @return the resulting hex string 74 | */ 75 | public static String toHex(byte[] bytes, int offset, int length, String separator) { 76 | StringBuilder result = new StringBuilder(); 77 | for (int i = 0; i < length; i++) { 78 | int unsignedByte = bytes[i + offset] & 0xff; 79 | 80 | if (unsignedByte < 16) { 81 | result.append("0"); 82 | } 83 | 84 | result.append(Integer.toHexString(unsignedByte)); 85 | if (separator != null && i + 1 < length) { 86 | result.append(separator); 87 | } 88 | } 89 | return result.toString(); 90 | } 91 | 92 | /** 93 | * Get the byte representation of an ASCII-HEX string. 94 | * 95 | * @param hexString 96 | * The string to convert to bytes 97 | * @return The byte representation of the ASCII-HEX string. 98 | */ 99 | public static byte[] toBytes(String hexString) { 100 | if (hexString == null || hexString.length() % 2 != 0) { 101 | throw new RuntimeException("Input string must contain an even number of characters"); 102 | } 103 | char[] hex = hexString.toCharArray(); 104 | int length = hex.length / 2; 105 | byte[] raw = new byte[length]; 106 | for (int i = 0; i < length; i++) { 107 | int high = Character.digit(hex[i * 2], 16); 108 | int low = Character.digit(hex[i * 2 + 1], 16); 109 | if (high < 0 || low < 0){ 110 | throw new RuntimeException("Invalid hex digit " + hex[i * 2] + hex[i * 2 + 1]); 111 | } 112 | int value = (high << 4) | low; 113 | if (value > 127) 114 | value -= 256; 115 | raw[i] = (byte) value; 116 | } 117 | return raw; 118 | } 119 | 120 | public static byte[] toBytesReversed( String hexString) { 121 | byte[] rawBytes = toBytes( hexString ); 122 | 123 | for ( int i = 0; i < rawBytes.length / 2;i++ ) { 124 | byte temp = rawBytes[ rawBytes.length - i - 1]; 125 | rawBytes[ rawBytes.length - i - 1] = rawBytes[ i ]; 126 | rawBytes[ i ] = temp; 127 | } 128 | 129 | return rawBytes; 130 | } 131 | 132 | public static void appendByteAsHex(StringBuilder sb, byte b) { 133 | int unsignedByte = b & 0xFF; 134 | if (unsignedByte < 16) { 135 | sb.append("0"); 136 | } 137 | sb.append(Integer.toHexString(unsignedByte)); 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/crypto/util/RefValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 PLACTAL. 3 | * 4 | * The MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package com.whaleex.api.client.util.crypto.util; 25 | 26 | /** 27 | * Created by swapnibble on 2017-09-28. 28 | */ 29 | 30 | public class RefValue { 31 | public T data; 32 | 33 | public RefValue(){ 34 | data = null; 35 | } 36 | 37 | public RefValue(T initialVal ){ 38 | data = initialVal; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/EosByteWriter.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | import java.util.Collection; 4 | 5 | public class EosByteWriter implements EosType.Writer { 6 | private byte[] _buf; 7 | private int _index; 8 | 9 | public EosByteWriter(int capacity) { 10 | _buf = new byte[capacity]; 11 | _index = 0; 12 | } 13 | 14 | public EosByteWriter(byte[] buf) { 15 | _buf = buf; 16 | _index = buf.length; 17 | } 18 | 19 | private void ensureCapacity(int capacity) { 20 | if (_buf.length - _index < capacity) { 21 | byte[] temp = new byte[_buf.length * 2 + capacity]; 22 | System.arraycopy(_buf, 0, temp, 0, _index); 23 | _buf = temp; 24 | } 25 | } 26 | 27 | @Override 28 | public void put(byte b) { 29 | ensureCapacity(1); 30 | _buf[_index++] = b; 31 | } 32 | 33 | @Override 34 | public void putShortLE(short value) { 35 | ensureCapacity(2); 36 | _buf[_index++] = (byte) (0xFF & (value)); 37 | _buf[_index++] = (byte) (0xFF & (value >> 8)); 38 | } 39 | 40 | @Override 41 | public void putIntLE(int value) { 42 | ensureCapacity(4); 43 | _buf[_index++] = (byte) (0xFF & (value)); 44 | _buf[_index++] = (byte) (0xFF & (value >> 8)); 45 | _buf[_index++] = (byte) (0xFF & (value >> 16)); 46 | _buf[_index++] = (byte) (0xFF & (value >> 24)); 47 | } 48 | 49 | 50 | @Override 51 | public void putLongLE(long value) { 52 | ensureCapacity(8); 53 | _buf[_index++] = (byte) (0xFFL & (value)); 54 | _buf[_index++] = (byte) (0xFFL & (value >> 8)); 55 | _buf[_index++] = (byte) (0xFFL & (value >> 16)); 56 | _buf[_index++] = (byte) (0xFFL & (value >> 24)); 57 | _buf[_index++] = (byte) (0xFFL & (value >> 32)); 58 | _buf[_index++] = (byte) (0xFFL & (value >> 40)); 59 | _buf[_index++] = (byte) (0xFFL & (value >> 48)); 60 | _buf[_index++] = (byte) (0xFFL & (value >> 56)); 61 | } 62 | 63 | 64 | @Override 65 | public void putBytes(byte[] value) { 66 | ensureCapacity(value.length); 67 | System.arraycopy(value, 0, _buf, _index, value.length); 68 | _index += value.length; 69 | } 70 | 71 | public void putBytes(byte[] value, int offset, int length) { 72 | ensureCapacity(length); 73 | System.arraycopy(value, offset, _buf, _index, length); 74 | _index += length; 75 | } 76 | 77 | 78 | @Override 79 | public byte[] toBytes() { 80 | byte[] bytes = new byte[_index]; 81 | System.arraycopy(_buf, 0, bytes, 0, _index); 82 | return bytes; 83 | } 84 | 85 | @Override 86 | public int length() { 87 | return _index; 88 | } 89 | 90 | 91 | @Override 92 | public void putString(String value){ 93 | if ( null == value ){ 94 | putVariableUInt( 0 ); 95 | return; 96 | } 97 | 98 | // array count 는 variable int 로 넣어야 한다. 99 | putVariableUInt( value.length() ); 100 | putBytes( value.getBytes() ); 101 | } 102 | 103 | @Override 104 | public void putCollection(Collection collection){ 105 | if ( null == collection){ 106 | putVariableUInt( 0 ); 107 | return; 108 | } 109 | 110 | // element count 는 variable int 로 넣어야 한다. 111 | putVariableUInt( collection.size() ); 112 | 113 | for ( EosType.Packer type : collection) { 114 | type.pack( this ); 115 | } 116 | } 117 | 118 | @Override 119 | public void putVariableUInt(long val ) { 120 | 121 | do { 122 | byte b = (byte)((val) & 0x7f); 123 | val >>= 7; 124 | b |= ( ((val > 0) ? 1 : 0 ) << 7 ); 125 | put(b); 126 | } while( val != 0 ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/EosType.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | import java.util.Collection; 4 | 5 | public interface EosType { 6 | class InsufficientBytesException extends Exception { 7 | private static final long serialVersionUID = 1L; 8 | } 9 | 10 | interface Packer { 11 | void pack(EosType.Writer writer); 12 | } 13 | 14 | interface Unpacker { 15 | void unpack(EosType.Reader reader) throws EosType.InsufficientBytesException; 16 | } 17 | 18 | interface Reader { 19 | byte get() throws EosType.InsufficientBytesException; 20 | int getShortLE() throws EosType.InsufficientBytesException; 21 | int getIntLE() throws EosType.InsufficientBytesException; 22 | long getLongLE() throws EosType.InsufficientBytesException; 23 | byte[] getBytes(int size) throws EosType.InsufficientBytesException; 24 | String getString() throws EosType.InsufficientBytesException; 25 | 26 | long getVariableUint() throws EosType.InsufficientBytesException; 27 | } 28 | 29 | interface Writer { 30 | void put(byte b); 31 | void putShortLE(short value); 32 | void putIntLE(int value); 33 | void putLongLE(long value); 34 | void putBytes(byte[] value); 35 | void putString(String value); 36 | byte[] toBytes(); 37 | int length(); 38 | 39 | void putCollection(Collection collection); 40 | 41 | void putVariableUInt(long val); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/TypeAccountName.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | 5 | public class TypeAccountName extends TypeName { 6 | private static final int MAX_ACCOUNT_NAME_LEN = 12; 7 | 8 | private static final char CHAR_NOT_ALLOWED = '.'; 9 | 10 | public TypeAccountName(String name) { 11 | super(name); 12 | 13 | if (! StringUtils.isEmpty( name )) { 14 | if (name.length() > MAX_ACCOUNT_NAME_LEN ) { 15 | throw new IllegalArgumentException("account name can only be 12 chars long: " + name) ; // changed from dawn3 16 | } 17 | 18 | if ( (name.indexOf( CHAR_NOT_ALLOWED) >= 0) && ! name.startsWith("eosio.") ){ 19 | throw new IllegalArgumentException("account name must not contain '.': " + name); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/TypeActionName.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | public class TypeActionName extends TypeName { 4 | public TypeActionName(long nameAsLong) { 5 | super(nameAsLong); 6 | } 7 | 8 | public TypeActionName(String name) { 9 | super(name); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/TypeChainId.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | import com.whaleex.api.client.util.crypto.digest.Sha256; 4 | 5 | public class TypeChainId { 6 | private final Sha256 mId; 7 | 8 | public TypeChainId() { 9 | mId = Sha256.ZERO_HASH; 10 | } 11 | 12 | byte [] getSha256FromHexStr(String str){ 13 | int len = str.length(); 14 | byte [] bytes = new byte[32]; 15 | for(int i=0;i= 'a' && c <= 'z' ) 13 | return (byte)((c - 'a') + 6); 14 | if( c >= '1' && c <= '5' ) 15 | return (byte)((c - '1') + 1); 16 | 17 | return (byte)0; 18 | } 19 | 20 | static public long string_to_name( String str ) { 21 | if ( null == str ) { 22 | return 0L; 23 | } 24 | 25 | int len = str.length(); 26 | long value = 0; 27 | 28 | for (int i = 0; i <= MAX_NAME_IDX; i++) { 29 | long c = 0; 30 | 31 | if( i < len && i <= MAX_NAME_IDX) c = char_to_symbol( str.charAt(i) ); 32 | 33 | if( i < MAX_NAME_IDX) { 34 | c &= 0x1f; 35 | c <<= 64-5*(i+1); 36 | } 37 | else { 38 | c &= 0x0f; 39 | } 40 | 41 | value |= c; 42 | } 43 | 44 | return value; 45 | } 46 | 47 | static public String name_to_string( long nameAsLong ) { 48 | long tmp = nameAsLong; 49 | 50 | char[] result = new char[MAX_NAME_IDX + 1]; 51 | Arrays.fill( result, ' '); 52 | 53 | for( int i = 0; i <= MAX_NAME_IDX; ++i ) { 54 | char c = CHAR_MAP.charAt( (int)(tmp & (i == 0 ? 0x0f : 0x1f)) ); 55 | result[MAX_NAME_IDX-i] = c; 56 | tmp >>= (i == 0 ? 4 : 5); 57 | } 58 | 59 | return String.valueOf( result ).replaceAll("[.]+$", ""); // remove trailing dot 60 | } 61 | 62 | public TypeName(long nameAsLong) { 63 | mValue = nameAsLong; 64 | } 65 | 66 | public TypeName(String name) { 67 | mValue = string_to_name( name); 68 | } 69 | 70 | @Override 71 | public void pack(EosType.Writer writer) { 72 | writer.putLongLE( mValue); 73 | } 74 | 75 | @Override 76 | public String toString(){ 77 | return name_to_string( mValue ); 78 | } 79 | 80 | public static void main(String[] args) { 81 | System.out.println(name_to_string(3806469521562716240l)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/TypePermissionLevel.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | public class TypePermissionLevel implements EosType.Packer { 4 | 5 | private TypeAccountName actor; 6 | 7 | private TypePermissionName permission; 8 | 9 | public TypePermissionLevel(String accountName, String permissionName) { 10 | actor = new TypeAccountName(accountName); 11 | permission = new TypePermissionName(permissionName); 12 | } 13 | 14 | @Override 15 | public void pack(EosType.Writer writer) { 16 | 17 | actor.pack(writer); 18 | permission.pack(writer); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/java/src/main/java/com/whaleex/api/client/util/types/TypePermissionName.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util.types; 2 | 3 | public class TypePermissionName extends TypeName { 4 | public TypePermissionName(String name) { 5 | super(name); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /sample/java/src/main/resources/keys.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/sample/java/src/main/resources/keys.properties -------------------------------------------------------------------------------- /sample/java/src/test/java/com/whaleex/api/client/util/ApiServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.WhaleexApiService; 4 | import com.whaleex.api.client.impl.WhaleexApiServiceImpl; 5 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 6 | import com.whaleex.api.client.pojo.response.*; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | import java.io.UnsupportedEncodingException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Optional; 15 | 16 | public class ApiServiceTest { 17 | WhaleexApiService apiService= new WhaleexApiServiceImpl(); 18 | 19 | // 登录获取access token 20 | @Test 21 | public void testLogin() throws IOException { 22 | final String cn = apiService.getUserToken("", "", ""); 23 | System.out.println(cn); 24 | 25 | } 26 | 27 | // 本地生成公私钥对 28 | @Test 29 | public void testGenKeyPair() throws IOException { 30 | apiService.generateKeys(); 31 | System.out.println("Key pairs are saved into key.properties"); 32 | } 33 | 34 | // 向服务器注册公钥 35 | @Test 36 | public void testRegisterPk() throws IOException { 37 | String token=""; 38 | apiService.registerPK(token,StoreUtils.getStorePublicKey()); 39 | } 40 | 41 | // 检查公钥绑定状态 42 | @Test 43 | public void testChectPk() throws IOException { 44 | String token=""; 45 | CommonResponse response = apiService.checkPkBindStatus(token, StoreUtils.getStorePublicKey()); 46 | System.out.println(response.getResult()); 47 | } 48 | 49 | // 生成API key 50 | @Test 51 | public void testGenAPIKey() throws IOException { 52 | String token=""; 53 | String apiKey = apiService.generateApiKey(token).getResult().toString(); 54 | StoreUtils.storeAPIKey(apiKey); 55 | } 56 | 57 | // 获取10个订单ID 58 | @Test 59 | public void testGetGlobleId() throws IOException { 60 | final CommonResponse idFromServer = apiService.getIdFromServer(0, 10); 61 | 62 | System.out.println(idFromServer.getResult()); 63 | } 64 | 65 | // 下单 66 | @Test 67 | public void testPlaseOrder(){ 68 | long orderId =92687347103399335l; 69 | String account ="yyyyyyyyyycc"; 70 | AutoOrderRequest orderRequest = new AutoOrderRequest(); 71 | orderRequest.setType(AutoOrderRequest.OrderType.sell_market); 72 | orderRequest.setAmount("100"); 73 | orderRequest.setPrice("0.01"); 74 | orderRequest.setSymbol("IQEBTC"); 75 | try { 76 | CommonResponse commonResponse = apiService.placeOrder(orderId, account, orderRequest); 77 | System.out.println(commonResponse); 78 | } catch (IOException e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | // 查询订单成交 84 | @Test 85 | public void testQueryOneOrderMatchResults(){ 86 | try { 87 | CommonResponse> pageResponseCommonResponse = apiService.queryOneOrderMatchResults(90193004341697964l); 88 | System.out.println(pageResponseCommonResponse); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | // 撤单 95 | @Test 96 | public void testCancelOrder(){ 97 | try { 98 | final CommonResponse commonResponse = apiService.cancelOrder(92687347103399305l); 99 | System.out.println(commonResponse); 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | // 获取订单详情 106 | @Test 107 | public void testOrderDtl(){ 108 | try { 109 | final CommonResponse commonResponse = apiService.getOrderDetail(92687347103399305l); 110 | System.out.println(commonResponse); 111 | } catch (IOException e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | 116 | // 获取用户资产 117 | @Test 118 | public void testGetUserAsset(){ 119 | try { 120 | final CommonResponse commonResponse = apiService.queryAssets(); 121 | System.out.println(commonResponse); 122 | } catch (IOException e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | 127 | @Test 128 | public void testSymbol() throws IOException { 129 | final List symbolResponse = apiService.getSymbol(); 130 | List symbolList = Optional.ofNullable(symbolResponse).orElseGet(ArrayList::new); 131 | System.out.println("baseCurrency\tbasePrecision\tquoteCurrency\tquotePrecision"); 132 | symbolList.forEach(symbol->{ 133 | System.out.println("\t"+symbol.getBaseCurrency() +"\t\t\t"+symbol.getBasePrecision()+ "\t\t\t"+symbol.getQuoteCurrency()+"\t\t\t"+symbol.getQuotePrecision()); 134 | }); 135 | } 136 | @Test 137 | public void testGetOpenOrders() throws IOException { 138 | CommonResponse openOrders = apiService.getOpenOrders(); 139 | System.out.println(openOrders); 140 | 141 | } 142 | @Test 143 | public void testOrderBook() throws IOException { 144 | final SimpleOrderBookResponse iqebtc = apiService.getOrderBook("IQEBTC"); 145 | System.out.println(iqebtc); 146 | } 147 | @Test 148 | public void testAllCancelOrder(){ 149 | try { 150 | final CommonResponse commonResponse = apiService.cancelOpenOrders(); 151 | System.out.println(commonResponse.toString()); 152 | } catch (UnsupportedEncodingException e) { 153 | e.printStackTrace(); 154 | } catch (IOException e) { 155 | e.printStackTrace(); 156 | } 157 | } 158 | @Test 159 | public void testAssetShow() throws IOException { 160 | CommonResponse assetsResponse = apiService.queryAssets(); 161 | AssetSummaryResponse assetSummary = assetsResponse.getResult(); 162 | if(assetSummary != null){ 163 | AssetSummaryResponse.Whale wal = assetSummary.getWal(); 164 | System.out.println("WAL 资产 "); 165 | System.out.println("可用: "+wal.getFreeAmount()); 166 | System.out.println("私募: "+wal.getPrivatePlacement()); 167 | System.out.println("锁仓: "+wal.getStakeAmount()); 168 | System.out.println("解锁中: "+wal.getUnStakingAmount()); 169 | 170 | List assetContents = Optional.ofNullable(assetSummary.getList().getContent()) 171 | .orElseGet(ArrayList::new); 172 | assetContents.forEach(asset->{ 173 | System.out.println(" 币种 : "+asset.getCurrency()); 174 | System.out.println(" 可用 : "+asset.getAvailableAmount()); 175 | System.out.println(" 持仓 : "+asset.getFixedAmount()); 176 | System.out.println(" 冻结 : "+asset.getFrozenAmount()); 177 | System.out.println(" 私募 : "+asset.getPrivatePlacement()); 178 | System.out.println(" 总计 : "+asset.getTotalAmount()); 179 | System.out.println("---------------------"); 180 | }); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /sample/java/src/test/java/com/whaleex/api/client/util/HttpUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.apache.http.HttpEntity; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | public class HttpUtilsTest { 10 | 11 | HttpUtils httpUtils = new HttpUtils(); 12 | 13 | @Test 14 | public void testLogin() throws IOException { 15 | String cn = httpUtils.doLoginRequest("15838261714", "qweqwe123", "CN"); 16 | System.out.println(cn); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sample/java/src/test/java/com/whaleex/api/client/util/PasswordUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class PasswordUtilsTest { 7 | 8 | HashUtils passwordUtils = new HashUtils(); 9 | 10 | @Test 11 | public void testHash(){ 12 | final String qweqwe123 = HashUtils.getHash("qweqwe123"); 13 | Assert.assertEquals("c9fe854ea69fc0a252340e152864b539b116c36cf1ac419652e1826c3071d5ed",qweqwe123); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sample/java/src/test/java/com/whaleex/api/client/util/SignUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import com.whaleex.api.client.pojo.Symbol; 4 | import com.whaleex.api.client.pojo.request.AutoOrderRequest; 5 | import com.whaleex.api.client.util.crypto.digest.Sha256; 6 | import com.whaleex.api.client.util.crypto.ec.EcDsa; 7 | import com.whaleex.api.client.util.crypto.ec.EcSignature; 8 | import com.whaleex.api.client.util.crypto.ec.EosPrivateKey; 9 | import com.whaleex.api.client.util.crypto.ec.EosPublicKey; 10 | import org.junit.Test; 11 | 12 | public class SignUtilsTest { 13 | 14 | @Test 15 | public void testSignOrder(){ 16 | 17 | AutoOrderRequest orderRequest = new AutoOrderRequest(); 18 | orderRequest.setSymbol("IQEOS"); 19 | orderRequest.setAmount("1"); 20 | orderRequest.setType(AutoOrderRequest.OrderType.sell_limit); 21 | orderRequest.setPrice("9999"); 22 | Symbol symbol = new Symbol(); 23 | symbol.setBaseContract("everipediaiq"); 24 | symbol.setBaseCurrency("IQ"); 25 | symbol.setQuoteContract("eosio.token"); 26 | symbol.setQuoteCurrency("EOS"); 27 | symbol.setQuotePrecision(4); 28 | symbol.setBasePrecision(3); 29 | 30 | final String sign = SignUtils.signDateOrder(orderRequest, "yyyyyyyyyycc", 89385080403558920l, symbol); 31 | System.out.println(sign); 32 | 33 | } 34 | @Test 35 | public void testPrivateKey(){ 36 | EosPrivateKey eosPrivateKey = new EosPrivateKey(); 37 | System.out.println(eosPrivateKey.toString()); 38 | System.out.println(eosPrivateKey.getPublicKey().toString()); 39 | EcSignature signature = EcDsa.sign(Sha256.from("".getBytes()), eosPrivateKey); 40 | System.out.println(signature); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sample/java/src/test/java/com/whaleex/api/client/util/StoreUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.whaleex.api.client.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class StoreUtilsTest { 7 | 8 | @Test 9 | public void testProp(){ 10 | System.out.println(StoreUtils.getStorePublicKey()); 11 | StoreUtils.storePublicKye("aaa"); 12 | Assert.assertEquals("aaa",StoreUtils.getStorePublicKey()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/nodejs/ApiKey.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/sample/nodejs/ApiKey.json -------------------------------------------------------------------------------- /sample/nodejs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "your WhaleEx UserName", 3 | "location": "CN (you can find in util/country.json)", 4 | "userEosAccount": "", 5 | "HOST": "api.whaleex.com", 6 | "ExEosAccount": "whaleexchang", 7 | "ExAccount": "whaleextrust", 8 | "EOScontract": "eosio.token", 9 | "IQcontract": "everipediaiq" 10 | } 11 | -------------------------------------------------------------------------------- /sample/nodejs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | generateKeys, //生成私钥和公钥 会保存在keys.json中 3 | registerPK, //注册公钥 4 | checkPkBindStatus, //查询pk绑定状态 5 | getApiKey, //获取ApiKey 并保存在ApiKey.json中 6 | getIdFromServer,//获取globalIds 7 | placeOrder,//下单 8 | getOpenOrders,//获取当前订单 9 | cancelOrder,//撤单 10 | getOrderDetail,//获取某一订单详情 11 | getHistoryOrder,//历史委托 12 | getExecOrder,//查询成交 13 | queryOneOrderMatchResults,//查询单一委托成交 14 | queryAssets,//获取用户资产 15 | queryOneAsset,//获取用户某一资产 16 | getTicker//获取最新行情 17 | getOrderBook//获取订单铺 18 | */ 19 | const WAL = require("./whaleex-api.js"); 20 | const Print = require("./util/console.js"); 21 | const Config = require("./config.json"); 22 | const U = require("./whaleex-sign.js"); 23 | const fs = require("fs-extra"); 24 | console.log("welcome WhaleEx API"); 25 | generateKeys = () => { 26 | WAL.generateKeys(); 27 | }; 28 | loopPkBindStatus = async (access_token, _reslove) => { 29 | let pkBindStatus; 30 | process.stdout.write( 31 | "checking pk bind status. after transfer, pk will be actived in 3-5 minutes" 32 | ); 33 | while (pkBindStatus !== "ACTIVED") { 34 | process.stdout.write("."); 35 | pkBindStatus = await WAL.checkPkBindStatus(access_token); 36 | await new Promise(reslove => { 37 | setTimeout(reslove, 2000); 38 | }); 39 | } 40 | if (pkBindStatus === "ACTIVED") { 41 | console.log("publicKey is Actived!"); 42 | const keys = U.getKeys() || {}; 43 | fs.writeFileSync( 44 | `ApiKey.json`, 45 | JSON.stringify({ ...keys, isPkActived: true }), 46 | "utf8" 47 | ); 48 | _reslove && _reslove(); 49 | } 50 | }; 51 | async function generateSignKeys() { 52 | const { user, location, userEosAccount } = Config; 53 | const keys = U.getKeys() || {}; 54 | if (!keys.APIKey || !keys.privateKey) { 55 | //step-2 用户登录 56 | let password = await Print.insertPassword(user); 57 | const token = await WAL.getUserToken(user, password, location); 58 | //step-3 获取APIKey 59 | await WAL.generateApiKey(token.access_token); 60 | //step-4 生成公钥,私钥 61 | await WAL.generateKeys(); 62 | const pkR = await WAL.registerPK(token.access_token); 63 | if (!!pkR) { 64 | return new Promise(reslove => { 65 | Print.checkPkStatus(() => { 66 | loopPkBindStatus(token.access_token, reslove); 67 | }); 68 | }); 69 | } 70 | } else if (!keys.isPkActived) { 71 | Print.alreadyGenerateApiKey(); 72 | Print.alreadyGenerate(keys.publicKey); 73 | let password = await Print.insertPassword(user); 74 | const token = await WAL.getUserToken(user, password, location); 75 | return new Promise(reslove => { 76 | Print.checkPkStatus(() => { 77 | loopPkBindStatus(token.access_token, reslove); 78 | }); 79 | }); 80 | } 81 | } 82 | createSymbolObj = (symbol, currencyList) => { 83 | const { baseCurrency, quoteCurrency, basePrecision, quotePrecision } = symbol; 84 | const B = currencyList.filter( 85 | ({ shortName }) => shortName === baseCurrency 86 | )[0]; 87 | const Q = currencyList.filter( 88 | ({ shortName }) => shortName === quoteCurrency 89 | )[0]; 90 | return { 91 | baseCurrency, 92 | quoteCurrency, 93 | basePrecision: B.decimals, 94 | quotePrecision: Q.decimals, 95 | baseContract: B.contract, 96 | quoteContract: Q.contract, 97 | baseToken: B.token, 98 | quoteToken: Q.token 99 | }; 100 | }; 101 | createOrder = (amount, price, symbol, type) => { 102 | return { amount, price, symbol, type }; 103 | }; 104 | runTrade = async function() { 105 | //step-1 查询交易对和币种 106 | let symbolList = await WAL.getSymnbol(); 107 | let currencyList = await WAL.getCurrency(); 108 | Print.showSymbolList(symbolList); 109 | Print.showCurrencyList(currencyList); 110 | //step-2 完善用户信息 111 | const { user, location, userEosAccount } = Config; 112 | if (!user || !location || !userEosAccount) { 113 | Print.needUserInfo(); 114 | return; 115 | } 116 | //step-3 获取APIKey 117 | //step-4 生成公钥,私钥 118 | await generateSignKeys(); 119 | //step-5 打印待下单交易对信息 120 | const symbol = symbolList[3]; 121 | Print.consoleSymbol(symbol); 122 | const orderBook = await WAL.getOrderBook(symbol.name); 123 | Print.consoleOrderBook(orderBook, symbol); 124 | const symbolMarket = await WAL.getTicker(symbol.name); 125 | Print.consoleMarket(symbolMarket, symbol); 126 | //step-6 打印用户资产 127 | const assets = await WAL.queryAssets(); 128 | Print.consoleAsset(assets); 129 | //step-7 取当前委托 130 | const orders = await WAL.getOpenOrders(0, 5); 131 | Print.consoleOrders(orders); 132 | //setp-8 获取历史成交 133 | const execOrders = await WAL.getExecOrder(0, 5, symbol.name); 134 | Print.consoleExecOrders(execOrders, symbol); 135 | //step-9 获取用户可用资产 136 | const _assetList = assets.filter( 137 | ({ availableAmount }) => +availableAmount > 0 138 | ); 139 | if (_assetList.length === 0) { 140 | process.exit(0); 141 | return; 142 | } 143 | //step-10 下单 144 | const symbolObj = createSymbolObj(symbol, currencyList); 145 | const idStore = await WAL.createIdStore(5); 146 | let orderList = [ 147 | createOrder("0.001", "99999", symbol.name, "sell-limit"), 148 | createOrder("1", "0.0001", symbol.name, "buy-limit"), 149 | createOrder("999999", "99999", symbol.name, "sell-limit") 150 | ]; 151 | let list = []; 152 | const orderResult = await Promise.all( 153 | orderList.map(async (order, idx) => { 154 | const orderId = await idStore.getId(); 155 | list.push(orderId); 156 | return WAL.placeOrder(orderId, userEosAccount, order, symbolObj); //下单 157 | }) 158 | ); 159 | Print.consolePlaceOrder(orderList, orderResult, list); 160 | //step-11 取委托明细 161 | let orderId = list[0]; 162 | const orderDetail = await WAL.getOrderDetail(orderId); 163 | Print.consoleOrderDetail([orderDetail]); 164 | //step-12 撤单 165 | const cancelOrder = await WAL.cancelOrder(orderId); 166 | Print.consoleCancelOrder(cancelOrder, orderId); 167 | //step-13 批量撤单 168 | const r = await WAL.cancelBatchBySymbol(symbol.name); 169 | Print.consoleCancelBatch(r); 170 | }; 171 | runTrade(); //交易简单流程 172 | -------------------------------------------------------------------------------- /sample/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whaleex-api-node", 3 | "version": "1.0.0", 4 | "description": "whaleex-api-node", 5 | "main": "index.js", 6 | "dependencies": { 7 | "axios": "0.18.0", 8 | "eosjs-ecc": "^4.0.4", 9 | "form-data": "2.3.3", 10 | "fs-extra": "^7.0.1", 11 | "mathjs": "5.2.3", 12 | "qs": "^6.5.2" 13 | }, 14 | "devDependencies": {}, 15 | "scripts": { 16 | "test": "npm test" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git:sdk/nodes-client.git" 21 | }, 22 | "author": "whaleex", 23 | "license": "ISC" 24 | } 25 | -------------------------------------------------------------------------------- /sample/nodejs/util/api.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhaleEx/API/0795cb8d57e9965225fbbe6ffb757d939643da48/sample/nodejs/util/api.js -------------------------------------------------------------------------------- /sample/nodejs/util/console.js: -------------------------------------------------------------------------------- 1 | const { HOST, ExAccount, userEosAccount } = require("../config.json"); 2 | function needUserInfo() { 3 | console.log( 4 | "> Please perfect personal information in config.json:【 user, location, userEosAccount 】" 5 | ); 6 | } 7 | function loginWelcome(username) { 8 | let _username = username.split(""); 9 | _username.splice(3, 4, "****"); 10 | console.log("> WhaleEx: login success."); 11 | } 12 | function pkGenerated(publicKey, msg) { 13 | console.log(msg); 14 | console.log("|"); 15 | console.log( 16 | "| Please transfer any amount of EOS to the below contract address with your bound EOS account." 17 | ); //本地公钥未激活, 通过小额转账激活 18 | console.log("| Contract Account:", ExAccount); //收款合约账户 19 | console.log("| memo:", "bind:" + publicKey + ":WhaleEx"); 20 | } 21 | function checkPkStatus(callBack) { 22 | process.stdout.write("I have transfered,query pk bind status(y/n):"); 23 | process.stdin.on("data", input => { 24 | input = input.toString().trim(); 25 | if (["Y", "y", "YES", "yes"].indexOf(input) > -1) { 26 | console.log("> STORED (not actived) | ACTIVED "); 27 | callBack && callBack(); 28 | } 29 | if (["N", "n", "NO", "no"].indexOf(input) > -1) process.exit(0); 30 | }); 31 | } 32 | function showTable(index, list, keys) { 33 | let items = {}; 34 | function Instance(obj) { 35 | keys.forEach(key => { 36 | this[key] = obj[key]; 37 | }); 38 | } 39 | list.forEach(i => { 40 | items[i[index]] = new Instance(i); 41 | }); 42 | console.table(items); 43 | } 44 | function insertPassword(user) { 45 | return new Promise(reslove => { 46 | let _username = user.split(""); 47 | _username.splice(3, 4, "****"); 48 | process.stdout.write( 49 | `> Welcome ${_username.join("")}, please insert password:` 50 | ); 51 | process.stdin.on("data", input => { 52 | input = input.toString().trim(); 53 | reslove(input); 54 | }); 55 | }); 56 | } 57 | function showSymbolList(list) { 58 | console.log("Pairs:"); 59 | showTable("name", list, [ 60 | "id", 61 | "baseCurrency", 62 | "quoteCurrency", 63 | "basePrecision", 64 | "quotePrecision", 65 | "lastPrice" 66 | ]); 67 | } 68 | function showCurrencyList(list) { 69 | console.log("Coin List:"); 70 | showTable("shortName", list, ["depositAddress", "contract"]); 71 | } 72 | function alreadyGenerate(publicKey) { 73 | console.log( 74 | `> WhaleEx: privateKey, publicKey already exist. (delete privateKey and publicKey in ApiKey.json to regenerate)` 75 | ); 76 | console.log(`> ---- memo: bind:${publicKey}:WhaleEx`); 77 | } 78 | function successGenerate() { 79 | console.log( 80 | `> WhaleEx: privateKey, publicKey generate success. (Please store your private key after encryption!)` 81 | ); 82 | } 83 | function alreadyGenerateApiKey() { 84 | console.log( 85 | "> WhaleEx: APIKey already exist. (delete APIKey in ApiKey.json to regenerate)" 86 | ); 87 | } 88 | function successGenerateApiKey(apikey) { 89 | console.log("> WhaleEx: APIKey generate success. APIKey: " + apikey); 90 | } 91 | function consoleSymbol(symbol) { 92 | console.log("Pair Info:"); 93 | showTable( 94 | "name", 95 | [symbol], 96 | ["baseCurrency", "quoteCurrency", "basePrecision", "quotePrecision"] 97 | ); 98 | } 99 | function consoleOrderBook(orderBook, symbol) { 100 | console.log("Order Book:"); 101 | const { asks, bids } = orderBook; 102 | let _asks = asks.reverse().map((i, idx) => { 103 | return { ...i, index: `Sell${asks.length - idx}` }; 104 | }); 105 | let _bids = bids.map((i, idx) => { 106 | return { ...i, index: `Buy${idx + 1}` }; 107 | }); 108 | showTable( 109 | "index", 110 | [ 111 | ..._asks, 112 | { index: "----", price: symbol.lastPrice, quantity: symbol.name }, 113 | ..._bids 114 | ], 115 | ["price", "quantity"] 116 | ); 117 | } 118 | function consoleMarket(symbolMarket, symbol) { 119 | console.log(symbol.name + "Pair Market:"); 120 | showTable( 121 | "name", 122 | [{ ...symbol, ...symbolMarket }], 123 | ["symbolId", "priceChangePercent", "lastPrice", "baseVolume"] 124 | ); 125 | showTable("name", [{ ...symbol, ...symbolMarket }], ["open", "high", "low"]); 126 | } 127 | function consoleAsset(assetList) { 128 | console.log("Your Assets:"); 129 | function Asset(totalAmount, fixedAmount, availableAmount, privatePlacement) { 130 | this.totalAmount = totalAmount; 131 | this.fixedAmount = fixedAmount; 132 | this.availableAmount = availableAmount; 133 | this.privatePlacement = privatePlacement; 134 | } 135 | var assets = {}; 136 | assetList.forEach(i => { 137 | const { 138 | currency, 139 | totalAmount, 140 | fixedAmount, 141 | availableAmount, 142 | privatePlacement 143 | } = i; 144 | if (+totalAmount === 0) { 145 | return; 146 | } 147 | assets[currency] = new Asset( 148 | totalAmount, 149 | fixedAmount, 150 | availableAmount, 151 | privatePlacement 152 | ); 153 | }); 154 | console.table(assets); 155 | } 156 | function consoleOrders(orderList) { 157 | console.log("Current Open Order:size=5"); 158 | function Order(symbolId, price, origQty, type, side) { 159 | this.symbolId = symbolId; 160 | this.price = price; 161 | this.origQty = origQty; 162 | this.type = type; 163 | this.side = side; 164 | } 165 | var orders = {}; 166 | orderList.forEach(i => { 167 | const { orderId, symbolId, price, origQty, type, side } = i; 168 | orders[orderId] = new Order(symbolId, price, origQty, type, side); 169 | }); 170 | console.table(orders); 171 | } 172 | function consoleOrderDetail(orderList) { 173 | if (orderList.length > 0 && orderList[0]) { 174 | const { orderId } = orderList[0]; 175 | console.log(`Open Order Detail:${orderId}`); 176 | showTable("orderId", orderList, [ 177 | "symbolId", 178 | "price", 179 | "origQty", 180 | "execQty", 181 | "type", 182 | "side", 183 | "status" 184 | ]); 185 | } 186 | } 187 | function consoleExecOrders(list, symbol) { 188 | console.log(symbol.name + "Transaction List:"); 189 | const _list = list.map(i => ({ 190 | ...i, 191 | typeStr: (i.type === 76 && "limit") || "market", 192 | sideStr: (i.side === 83 && "sell") || "buy" 193 | })); 194 | showTable("orderId", _list, [ 195 | "price", 196 | "quantity", 197 | "volume", 198 | "typeStr", 199 | "sideStr" 200 | ]); 201 | } 202 | function consolePlaceOrder(orderList, orderResult, orderIdList) { 203 | console.log("Current Order Detail:"); 204 | const list = orderList.map((i, idx) => { 205 | return { ...i, ...orderResult[idx], orderId: orderIdList[idx] }; 206 | }); 207 | showTable("orderId", list, ["symbol", "price", "amount", "type", "message"]); 208 | } 209 | function consoleCancelOrder(r, orderId) { 210 | if (r.returnCode === "0") { 211 | console.log(orderId + " Cancel withdrawal Success!"); 212 | } else { 213 | console.log(orderId + " Cancel withdrawal Fail!" + r.message); 214 | } 215 | } 216 | function consoleCancelBatch(r) { 217 | if (r.returnCode === "0") { 218 | console.log(" Batch Cancel withdrawal Success!"); 219 | } else { 220 | console.log(" Batch Cancel withdrawal Fail!" + r.message); 221 | } 222 | } 223 | module.exports = { 224 | needUserInfo, 225 | loginWelcome, 226 | pkGenerated, 227 | checkPkStatus, 228 | showSymbolList, 229 | showCurrencyList, 230 | insertPassword, 231 | alreadyGenerate, 232 | successGenerate, 233 | alreadyGenerateApiKey, 234 | successGenerateApiKey, 235 | consoleSymbol, 236 | consoleOrderBook, 237 | consoleMarket, 238 | consoleAsset, 239 | consoleOrders, 240 | consoleExecOrders, 241 | consolePlaceOrder, 242 | consoleOrderDetail, 243 | consoleCancelOrder, 244 | consoleCancelBatch 245 | }; 246 | -------------------------------------------------------------------------------- /sample/nodejs/util/country.json: -------------------------------------------------------------------------------- 1 | [ 2 | "MG-261-Madagascar", 3 | "AE-971-United Arab Emirates", 4 | "UA-380-Ukraine", 5 | "GS-500-South Georgia and the South Sandwich Islands", 6 | "SM-378-San Marino", 7 | "MX-52-Mexico", 8 | "LK-94-Sri Lanka", 9 | "DZ-213-Algeria", 10 | "WF-681-Wallis and Futuna", 11 | "CU-53-Cuba", 12 | "PF-689-French Polynesia", 13 | "AT-43-Austria", 14 | "SK-421-Slovakia", 15 | "VE-58-Venezuela (Bolivarian Republic of)", 16 | "LR-231-Liberia", 17 | "IN-91-India", 18 | "MT-356-Malta", 19 | "GE-995-Georgia", 20 | "VU-678-Vanuatu", 21 | "NA-264-Namibia", 22 | "CC-61-Cocos (Keeling) Islands", 23 | "AO-244-Angola", 24 | "CV-238-Cabo Verde", 25 | "HU-36-Hungary", 26 | "EG-20-Egypt", 27 | "FO-298-Faroe Islands", 28 | "CD-243-Congo (Democratic Republic of the)", 29 | "AW-297-Aruba", 30 | "ET-251-Ethiopia", 31 | "HM--Heard Island and McDonald Islands", 32 | "BW-267-Botswana", 33 | "CI-225-Côte d'Ivoire", 34 | "CF-236-Central African Republic", 35 | "BD-880-Bangladesh", 36 | "PW-680-Palau", 37 | "NE-227-Niger", 38 | "CA-1-Canada", 39 | "PG-675-Papua New Guinea", 40 | "NR-674-Nauru", 41 | "MF-590-Saint Martin (French part)", 42 | "BN-673-Brunei Darussalam", 43 | "SL-232-Sierra Leone", 44 | "TN-216-Tunisia", 45 | "EC-593-Ecuador", 46 | "MZ-258-Mozambique", 47 | "KP-850-Korea (Democratic People's Republic of)", 48 | "BR-55-Brazil", 49 | "CM-237-Cameroon", 50 | "OM-968-Oman", 51 | "TJ-992-Tajikistan", 52 | "KE-254-Kenya", 53 | "EH-212-Western Sahara", 54 | "KG-996-Kyrgyzstan", 55 | "IS-354-Iceland", 56 | "TD-235-Chad", 57 | "HK-852-Hong Kong", 58 | "YE-967-Yemen", 59 | "BH-973-Bahrain", 60 | "BV--Bouvet Island", 61 | "TO-676-Tonga", 62 | "US-1-United States of America", 63 | "SZ-268-Swaziland", 64 | "AZ-994-Azerbaijan", 65 | "RO-40-Romania", 66 | "SI-386-Slovenia", 67 | "JO-962-Jordan", 68 | "TZ-255-Tanzania, United Republic of", 69 | "SH-290-Saint Helena, Ascension and Tristan da Cunha", 70 | "LT-370-Lithuania", 71 | "CK-682-Cook Islands", 72 | "CR-506-Costa Rica", 73 | "NP-977-Nepal", 74 | "IE-353-Ireland", 75 | "MO-853-Macao", 76 | "MU-230-Mauritius", 77 | "AD-376-Andorra", 78 | "ZW-263-Zimbabwe", 79 | "AU-61-Australia", 80 | "DO-1809-Dominican Republic", 81 | "VA-379-Holy See", 82 | "BT-975-Bhutan", 83 | "MM-95-Myanmar", 84 | "IL-972-Israel", 85 | "PL-48-Poland", 86 | "RW-250-Rwanda", 87 | "WS-685-Samoa", 88 | "AL-355-Albania", 89 | "CH-41-Switzerland", 90 | "ES-34-Spain", 91 | "MR-222-Mauritania", 92 | "BS-1242-Bahamas", 93 | "MC-377-Monaco", 94 | "UM--United States Minor Outlying Islands", 95 | "AR-54-Argentina", 96 | "TT-1868-Trinidad and Tobago", 97 | "YT-262-Mayotte", 98 | "RS-381-Serbia", 99 | "MV-960-Maldives", 100 | "PN-64-Pitcairn", 101 | "SC-248-Seychelles", 102 | "VC-1784-Saint Vincent and the Grenadines", 103 | "GH-233-Ghana", 104 | "TC-1649-Turks and Caicos Islands", 105 | "ST-239-Sao Tome and Principe", 106 | "BF-226-Burkina Faso", 107 | "GM-220-Gambia", 108 | "RE-262-Réunion", 109 | "PT-351-Portugal", 110 | "BQ-5997-Bonaire, Sint Eustatius and Saba", 111 | "HN-504-Honduras", 112 | "VN-84-Viet Nam", 113 | "KI-686-Kiribati", 114 | "CL-56-Chile", 115 | "TM-993-Turkmenistan", 116 | "HT-509-Haiti", 117 | "GT-502-Guatemala", 118 | "KN-1869-Saint Kitts and Nevis", 119 | "BY-375-Belarus", 120 | "PM-508-Saint Pierre and Miquelon", 121 | "KR-82-Korea (Republic of)", 122 | "BE-32-Belgium", 123 | "AF-93-Afghanistan", 124 | "DJ-253-Djibouti", 125 | "MY-60-Malaysia", 126 | "LU-352-Luxembourg", 127 | "SE-46-Sweden", 128 | "GU-1671-Guam", 129 | "SR-597-Suriname", 130 | "MD-373-Moldova (Republic of)", 131 | "ER-291-Eritrea", 132 | "JM-1876-Jamaica", 133 | "MP-1670-Northern Mariana Islands", 134 | "SN-221-Senegal", 135 | "TH-66-Thailand", 136 | "MQ-596-Martinique", 137 | "SJ-4779-Svalbard and Jan Mayen", 138 | "PA-507-Panama", 139 | "KY-1345-Cayman Islands", 140 | "ZM-260-Zambia", 141 | "GN-224-Guinea", 142 | "NI-505-Nicaragua", 143 | "SB-677-Solomon Islands", 144 | "GB-44-United Kingdom of Great Britain and Northern Ireland", 145 | "GI-350-Gibraltar", 146 | "FJ-679-Fiji", 147 | "LY-218-Libya", 148 | "PR-1787-Puerto Rico", 149 | "NF-672-Norfolk Island", 150 | "BM-1441-Bermuda", 151 | "GQ-240-Equatorial Guinea", 152 | "GR-30-Greece", 153 | "GW-245-Guinea-Bissau", 154 | "UY-598-Uruguay", 155 | "MK-389-Macedonia (the former Yugoslav Republic of)", 156 | "SS-211-South Sudan", 157 | "MW-265-Malawi", 158 | "AS-1684-American Samoa", 159 | "HR-385-Croatia", 160 | "AM-374-Armenia", 161 | "BA-387-Bosnia and Herzegovina", 162 | "TW-886-Taiwan", 163 | "PK-92-Pakistan", 164 | "GL-299-Greenland", 165 | "MS-1664-Montserrat", 166 | "MH-692-Marshall Islands", 167 | "UZ-998-Uzbekistan", 168 | "TG-228-Togo", 169 | "KM-269-Comoros", 170 | "BL-590-Saint Barthélemy", 171 | "GF-594-French Guiana", 172 | "KZ-76-Kazakhstan", 173 | "SX-1721-Sint Maarten (Dutch part)", 174 | "JE-44-Jersey", 175 | "CN-86-China", 176 | "MN-976-Mongolia", 177 | "BI-257-Burundi", 178 | "LC-1758-Saint Lucia", 179 | "SY-963-Syrian Arab Republic", 180 | "FK-500-Falkland Islands (Malvinas)", 181 | "CZ-420-Czech Republic", 182 | "TV-688-Tuvalu", 183 | "BJ-229-Benin", 184 | "IR-98-Iran (Islamic Republic of)", 185 | "GG-44-Guernsey", 186 | "FR-33-France", 187 | "LA-856-Lao People's Democratic Republic", 188 | "XK-383-Republic of Kosovo", 189 | "NC-687-New Caledonia", 190 | "SG-65-Singapore", 191 | "LS-266-Lesotho", 192 | "KW-965-Kuwait", 193 | "AQ-672-Antarctica", 194 | "CO-57-Colombia", 195 | "KH-855-Cambodia", 196 | "ZA-27-South Africa", 197 | "CW-599-Curaçao", 198 | "LB-961-Lebanon", 199 | "SA-966-Saudi Arabia", 200 | "NZ-64-New Zealand", 201 | "LV-371-Latvia", 202 | "NG-234-Nigeria", 203 | "LI-423-Liechtenstein", 204 | "VI-1 340-Virgin Islands (U.S.)", 205 | "TR-90-Turkey", 206 | "AI-1264-Anguilla", 207 | "SO-252-Somalia", 208 | "BO-591-Bolivia (Plurinational State of)", 209 | "DK-45-Denmark", 210 | "GD-1473-Grenada", 211 | "IT-39-Italy", 212 | "BB-1246-Barbados", 213 | "UG-256-Uganda", 214 | "BZ-501-Belize", 215 | "CG-242-Congo", 216 | "ML-223-Mali", 217 | "SD-249-Sudan", 218 | "FI-358-Finland", 219 | "BG-359-Bulgaria", 220 | "FM-691-Micronesia (Federated States of)", 221 | "SV-503-El Salvador", 222 | "PS-970-Palestine, State of", 223 | "CY-357-Cyprus", 224 | "MA-212-Morocco", 225 | "TK-690-Tokelau", 226 | "IQ-964-Iraq", 227 | "EE-372-Estonia", 228 | "TL-670-Timor-Leste", 229 | "TF--French Southern Territories", 230 | "IO-246-British Indian Ocean Territory", 231 | "NU-683-Niue", 232 | "QA-974-Qatar", 233 | "NO-47-Norway", 234 | "PE-51-Peru", 235 | "AX-358-Åland Islands", 236 | "RU-7-Russian Federation", 237 | "GY-592-Guyana", 238 | "DE-49-Germany", 239 | "AG-1268-Antigua and Barbuda", 240 | "GP-590-Guadeloupe", 241 | "CX-61-Christmas Island", 242 | "JP-81-Japan", 243 | "IM-44-Isle of Man", 244 | "ME-382-Montenegro", 245 | "VG-1284-Virgin Islands (British)", 246 | "DM-1767-Dominica", 247 | "ID-62-Indonesia", 248 | "PH-63-Philippines", 249 | "PY-595-Paraguay", 250 | "GA-241-Gabon", 251 | "NL-31-Netherlands" 252 | ] 253 | -------------------------------------------------------------------------------- /sample/nodejs/whaleex-sign.js: -------------------------------------------------------------------------------- 1 | const ecc = require("eosjs-ecc"); 2 | const crypto = require("crypto"); 3 | const hash = crypto.createHash("sha256"); 4 | const fs = require("fs-extra"); 5 | const Long = require("long"); 6 | const math = require("mathjs"); 7 | math.config({ number: "BigNumber" }); 8 | const { HOST, ExEosAccount } = require("./config.json"); 9 | const DEFAULT = params => { 10 | const keys = getKeys(); 11 | const { privateKey, publicKey: pk, APIKey } = keys; 12 | return { 13 | timestamp: new Date().getTime(), 14 | APIKey, 15 | pk, 16 | ...params 17 | }; 18 | }; 19 | const HANDLER = { 20 | "/api/v1/order/orders": DEFAULT, 21 | "/api/auth/pk/status": params => { 22 | const keys = getKeys(); 23 | const { publicKey: pk } = keys; 24 | return { 25 | pk, 26 | ...params 27 | }; 28 | }, 29 | "/api/v1/order/matchresults": DEFAULT, 30 | DEFAULT 31 | }; 32 | // 将各类数据打包成二进制流 33 | const Packer = function() { 34 | this.buf = Buffer.alloc(0); 35 | this.updateStr = function(str) { 36 | this.buf = Buffer.concat([this.buf, Buffer.from(str)]); 37 | return this; 38 | }; 39 | this.updateInt8 = function(i) { 40 | var tmp = Buffer.alloc(1); 41 | tmp.writeInt8(i, 0); 42 | this.buf = Buffer.concat([this.buf, tmp]); 43 | return this; 44 | }; 45 | this.updateInt16 = function(i) { 46 | var tmp = Buffer.alloc(2); 47 | tmp.writeInt16LE(i, 0); 48 | this.buf = Buffer.concat([this.buf, tmp]); 49 | return this; 50 | }; 51 | this.updateInt32 = function(i) { 52 | var tmp = Buffer.alloc(4); 53 | tmp.writeInt32LE(i, 0); 54 | this.buf = Buffer.concat([this.buf, tmp]); 55 | return this; 56 | }; 57 | this.updateInt64 = function(str) { 58 | var tmp = Buffer.alloc(8); 59 | var long = Long.fromString(str, 10); 60 | this.buf = Buffer.concat([this.buf, Buffer.from(long.toBytesLE())]); 61 | return this; 62 | }; 63 | this.finalize = function() { 64 | return this.buf; 65 | }; 66 | this.clear = function() { 67 | this.buf = Buffer.alloc(0); 68 | }; 69 | }; 70 | function getHandle(path) { 71 | return HANDLER[path] || HANDLER.DEFAULT; 72 | } 73 | function getHash() { 74 | return crypto.createHash("sha256"); 75 | } 76 | function multiply(m, n, decimal, ceil) { 77 | let result = math.eval(`${m} * ${n} * ${Math.pow(10, decimal)}`); 78 | if (ceil) { 79 | return String(Math.ceil(result)); 80 | } 81 | return String(result).split(".")[0]; 82 | } 83 | function getKeys() { 84 | const keys = fs.readFileSync("ApiKey.json", "utf8") || "{}"; 85 | const keysObj = JSON.parse(keys); 86 | return keysObj; 87 | } 88 | function signOrder(orderId, post, timestamp, account, symbolObj) { 89 | const { 90 | baseToken = "IQ", 91 | quoteToken = "EOS", 92 | basePrecision = 3, 93 | quotePrecision = 4, 94 | baseContract, 95 | quoteContract 96 | } = symbolObj; 97 | // "IQ/EOS for example" 98 | var pack = new Packer(); 99 | pack 100 | .updateStr(account) 101 | .updateStr(ExEosAccount) 102 | .updateInt64("" + orderId) 103 | .updateInt32(parseInt(timestamp / 1000)); 104 | if (post.type == "buy-limit") { 105 | pack 106 | .updateStr(quoteContract) 107 | .updateStr(quoteToken) 108 | .updateInt64( 109 | multiply(post.price, post.amount, quotePrecision /*EOS链上精度*/, true) 110 | ) 111 | .updateStr(baseContract) 112 | .updateStr(baseToken) 113 | .updateInt64(multiply(1, post.amount, basePrecision /*IQ链上精度*/)); 114 | } 115 | if (post.type == "sell-limit") { 116 | pack 117 | .updateStr(baseContract) 118 | .updateStr(baseToken) 119 | .updateInt64(multiply(1, post.amount, basePrecision)) 120 | .updateStr(quoteContract) 121 | .updateStr(quoteToken) 122 | .updateInt64(multiply(post.price, post.amount, quotePrecision)); 123 | } 124 | if (post.type == "buy-market") { 125 | pack 126 | .updateStr(quoteContract) 127 | .updateStr(quoteToken) 128 | .updateInt64(multiply(1, post.amount, quotePrecision)) 129 | .updateStr(baseContract) 130 | .updateStr(baseToken) 131 | .updateInt64("0"); 132 | } 133 | if (post.type == "sell-market") { 134 | pack 135 | .updateStr(baseContract) 136 | .updateStr(baseToken) 137 | .updateInt64(multiply(1, post.amount, basePrecision)) 138 | .updateStr(quoteContract) 139 | .updateStr(quoteToken) 140 | .updateInt64("0"); 141 | } 142 | pack.updateInt16(10).updateInt16(10); 143 | 144 | var buf = pack.finalize(); 145 | var hashData = getHash() 146 | .update(buf) 147 | .digest("hex"); 148 | var pack1 = new Packer(); 149 | let { privateKey } = getKeys(); 150 | var sig = ecc.signHash(hashData, privateKey); 151 | return sig; 152 | } 153 | function sort(map) { 154 | var sortedKeys = []; 155 | for (i in map) { 156 | sortedKeys.push(i); 157 | } 158 | sortedKeys.sort(); 159 | var params = ""; 160 | for (i = 0; i < sortedKeys.length; i++) { 161 | params = params + "&" + sortedKeys[i] + "=" + map[sortedKeys[i]]; 162 | } 163 | params = params.slice(1); 164 | return params; 165 | } 166 | function signData(method, path, params = {}) { 167 | let { privateKey } = getKeys(); 168 | // console.log("signData", method, path, params); 169 | let map = getHandle(path)(params); 170 | var params = sort(map); 171 | var data = `${method.toUpperCase()}\napi.whaleex.com\n${path}\n${encodeURIComponent( 172 | params 173 | )}`; 174 | var sig = ecc.sign(data, privateKey); 175 | params += "&Signature=" + sig; 176 | return params; 177 | } 178 | function signDataOrder(orderId, order, account, symbolObj = {}) { 179 | let { APIKey, publicKey: pk } = getKeys(); 180 | var map = { 181 | APIKey, 182 | timestamp: new Date().getTime(), 183 | pk, 184 | orderId: orderId 185 | }; 186 | var params = sort(map); 187 | var sig = signOrder(orderId, order, map.timestamp, account, symbolObj); 188 | params = params + "&Signature=" + sig; 189 | return params; 190 | } 191 | module.exports = { 192 | signData, 193 | signOrder, 194 | sort, 195 | signDataOrder, 196 | getKeys 197 | }; 198 | --------------------------------------------------------------------------------