diff --git a/README.md b/README.md index de1e4028..0d7a1cce 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Build ``` - git clone https://github.com/PlatONnetwork/client-sdk-java.git + git clone https://github.com/AlayaNetwork/client-sdk-java.git cd client-sdk-java/ ./gradlew clean jar //Generate jar package ./gradlew clean distZip //Generate code generation skeleton tool @@ -20,14 +20,14 @@ com.alaya.sdk core - 0.16.0.0 + 0.16.1.0 ``` or ``` -compile "com.alaya.sdk:core:0.16.0.0" +compile "com.alaya.sdk:core:0.16.1.0" ``` * use in project diff --git a/codegen/src/main/java/com/platon/codegen/TruffleJsonFunctionWrapperGenerator.java b/codegen/src/main/java/com/platon/codegen/TruffleJsonFunctionWrapperGenerator.java index 0c9c2675..a787aa58 100644 --- a/codegen/src/main/java/com/platon/codegen/TruffleJsonFunctionWrapperGenerator.java +++ b/codegen/src/main/java/com/platon/codegen/TruffleJsonFunctionWrapperGenerator.java @@ -118,9 +118,13 @@ private void generate() throws IOException, ClassNotFoundException { } else { addresses = Collections.EMPTY_MAP; } + String bytecode = c.getBytecode(); + if(!Strings.isBlank(bytecode) && bytecode.startsWith("0x")){ + bytecode = bytecode.substring(2); + } new SolidityFunctionWrapper(useJavaNativeTypes) .generateJavaFiles(contractName, - c.getBytecode(), + bytecode, c.getAbi(), destinationDirLocation.toString(), basePackageName, diff --git a/core/src/main/java/com/platon/contracts/ppos/BaseContract.java b/core/src/main/java/com/platon/contracts/ppos/BaseContract.java index 66f35ff6..d5ab9ec8 100644 --- a/core/src/main/java/com/platon/contracts/ppos/BaseContract.java +++ b/core/src/main/java/com/platon/contracts/ppos/BaseContract.java @@ -30,10 +30,13 @@ import com.platon.tx.ReadonlyTransactionManager; import com.platon.tx.TransactionManager; import com.platon.tx.exceptions.ContractCallException; +import com.platon.tx.exceptions.PlatonCallException; +import com.platon.tx.exceptions.PlatonCallTimeoutException; import com.platon.tx.gas.ContractGasProvider; import com.platon.tx.gas.GasProvider; import com.platon.utils.JSONUtil; import com.platon.utils.Numeric; +import com.platon.utils.Strings; import org.bouncycastle.util.encoders.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,6 +90,20 @@ private CallResponse executeCallObjectValueReturn(Function function, Clas DefaultBlockParameterName.LATEST) .send(); + //判断底层返回的错误信息是否包含超时信息 + if(ethCall.hasError()){ + Response.Error error = ethCall.getError(); + String message = error.getMessage(); + String lowMessage = !Strings.isBlank(message)? message.toLowerCase() : null; + //包含timeout则抛超时异常,其他错误则直接抛出runtime异常 + if(!Strings.isBlank(lowMessage) + && lowMessage.contains("timeout")){ + throw new PlatonCallTimeoutException(error.getCode(),error.getMessage(),ethCall); + } else { + throw new PlatonCallException(error.getCode(),error.getMessage(),ethCall); + } + } + String result = Numeric.cleanHexPrefix(ethCall.getValue()); if(result==null || "".equals(result)){ throw new ContractCallException("Empty value (0x) returned from contract"); @@ -131,6 +148,20 @@ private CallResponse> executeCallListValueReturn(Function function, DefaultBlockParameterName.LATEST) .send(); + //判断底层返回的错误信息是否包含超时信息 + if(ethCall.hasError()){ + Response.Error error = ethCall.getError(); + String message = error.getMessage(); + String lowMessage = !Strings.isBlank(message)? message.toLowerCase() : null; + //包含timeout则抛超时异常,其他错误则直接抛出runtime异常 + if(!Strings.isBlank(lowMessage) + && lowMessage.contains("timeout")){ + throw new PlatonCallTimeoutException(error.getCode(),error.getMessage(),ethCall); + } else { + throw new PlatonCallException(error.getCode(),error.getMessage(),ethCall); + } + } + String result = Numeric.cleanHexPrefix(ethCall.getValue()); if(result==null || "".equals(result)){ throw new ContractCallException("Empty value (0x) returned from contract"); diff --git a/core/src/main/java/com/platon/protocol/admin/Admin.java b/core/src/main/java/com/platon/protocol/admin/Admin.java index 533bd259..eb19e636 100644 --- a/core/src/main/java/com/platon/protocol/admin/Admin.java +++ b/core/src/main/java/com/platon/protocol/admin/Admin.java @@ -2,42 +2,60 @@ import com.platon.protocol.Web3j; import com.platon.protocol.Web3jService; -import com.platon.protocol.admin.methods.response.NewAccountIdentifier; -import com.platon.protocol.admin.methods.response.PersonalListAccounts; -import com.platon.protocol.admin.methods.response.PersonalUnlockAccount; +import com.platon.protocol.admin.methods.response.*; import com.platon.protocol.core.Request; +import com.platon.protocol.core.methods.response.PlatonSignTransaction; import com.platon.protocol.core.methods.request.Transaction; import com.platon.protocol.core.methods.response.PlatonSendTransaction; +import com.platon.protocol.core.methods.response.VoidResponse; import java.math.BigInteger; import java.util.concurrent.ScheduledExecutorService; /** - * JSON-RPC Request object building factory for common Parity and Geth. + * JSON-RPC Request object building factory for common Parity and Geth. */ public interface Admin extends Web3j { static Admin build(Web3jService web3jService) { return new JsonRpc2_0Admin(web3jService); } - + static Admin build( Web3jService web3jService, long pollingInterval, ScheduledExecutorService scheduledExecutorService) { return new JsonRpc2_0Admin(web3jService, pollingInterval, scheduledExecutorService); } - public Request personalListAccounts(); - - public Request personalNewAccount(String password); - - public Request personalUnlockAccount( + Request personalImportRawKey(String keydata, String password); + + Request personalLockAccount(String accountId); + + Request personalSign(String message, String accountId, String password); + + Request personalSignAndSendTransaction(Transaction transaction, String password); + + Request personalSignTransaction(Transaction transaction, String password); + + Request personalEcRecover(String message, String signiture); + + Request personalListWallets(); + + Request personalListAccounts(); + + Request personalNewAccount(String password); + + Request personalUnlockAccount( String address, String passphrase, BigInteger duration); - - public Request personalUnlockAccount( + + Request personalUnlockAccount( String address, String passphrase); - - public Request personalSendTransaction( + + Request personalOpenWallet(String url, String passphrase); + + Request personalSendTransaction( Transaction transaction, String password); -} + Request txPoolContent(); + +} diff --git a/core/src/main/java/com/platon/protocol/admin/JsonRpc2_0Admin.java b/core/src/main/java/com/platon/protocol/admin/JsonRpc2_0Admin.java index 16d511d0..fb52eec2 100644 --- a/core/src/main/java/com/platon/protocol/admin/JsonRpc2_0Admin.java +++ b/core/src/main/java/com/platon/protocol/admin/JsonRpc2_0Admin.java @@ -1,13 +1,13 @@ package com.platon.protocol.admin; import com.platon.protocol.Web3jService; -import com.platon.protocol.admin.methods.response.NewAccountIdentifier; -import com.platon.protocol.admin.methods.response.PersonalListAccounts; -import com.platon.protocol.admin.methods.response.PersonalUnlockAccount; +import com.platon.protocol.admin.methods.response.*; import com.platon.protocol.core.JsonRpc2_0Web3j; import com.platon.protocol.core.Request; +import com.platon.protocol.core.methods.response.PlatonSignTransaction; import com.platon.protocol.core.methods.request.Transaction; import com.platon.protocol.core.methods.response.PlatonSendTransaction; +import com.platon.protocol.core.methods.response.VoidResponse; import java.math.BigInteger; import java.util.ArrayList; @@ -24,12 +24,78 @@ public class JsonRpc2_0Admin extends JsonRpc2_0Web3j implements Admin { public JsonRpc2_0Admin(Web3jService web3jService) { super(web3jService); } - + public JsonRpc2_0Admin(Web3jService web3jService, long pollingInterval, - ScheduledExecutorService scheduledExecutorService) { + ScheduledExecutorService scheduledExecutorService) { super(web3jService, pollingInterval, scheduledExecutorService); } + @Override + public Request personalImportRawKey(String keydata, String password) { + return new Request<>( + "personal_importRawKey", + Arrays.asList(keydata, password), + web3jService, + PersonalImportRawKey.class); + } + + @Override + public Request personalLockAccount(String accountId) { + return new Request<>( + "personal_lockAccount", + Arrays.asList(accountId), + web3jService, + BooleanResponse.class); + } + + @Override + public Request personalSign( + String message, String accountId, String password) { + return new Request<>( + "personal_sign", + Arrays.asList(message, accountId, password), + web3jService, + PersonalSign.class); + } + + @Override + public Request personalSignAndSendTransaction( + Transaction transaction, String password) { + return new Request<>( + "personal_signAndSendTransaction", + Arrays.asList(transaction, password), + web3jService, + PersonalSign.class); + } + + @Override + public Request personalSignTransaction(Transaction transaction, String password) { + return new Request<>( + "personal_signTransaction", + Arrays.asList(transaction, password), + web3jService, + PlatonSignTransaction.class); + } + + @Override + public Request personalEcRecover( + String hexMessage, String signedMessage) { + return new Request<>( + "personal_ecRecover", + Arrays.asList(hexMessage, signedMessage), + web3jService, + PersonalEcRecover.class); + } + + @Override + public Request personalListWallets() { + return new Request<>( + "personal_listWallets", + Collections.emptyList(), + web3jService, + PersonalListWallets.class); + } + @Override public Request personalListAccounts() { return new Request<>( @@ -46,7 +112,7 @@ public Request personalNewAccount(String password) { Arrays.asList(password), web3jService, NewAccountIdentifier.class); - } + } @Override public Request personalUnlockAccount( @@ -55,7 +121,7 @@ public Request personalUnlockAccount( List attributes = new ArrayList<>(3); attributes.add(accountId); attributes.add(password); - + if (duration != null) { // Parity has a bug where it won't support a duration // See https://github.com/ethcore/parity/issues/1215 @@ -64,21 +130,30 @@ public Request personalUnlockAccount( // we still need to include the null value, otherwise Parity rejects request attributes.add(null); } - + return new Request<>( "personal_unlockAccount", attributes, web3jService, PersonalUnlockAccount.class); } - + @Override public Request personalUnlockAccount( String accountId, String password) { - + return personalUnlockAccount(accountId, password, null); } - + + @Override + public Request personalOpenWallet(String url, String passphrase) { + return new Request<>( + "personal_openWallet", + Arrays.asList(url, passphrase), + web3jService, + VoidResponse.class); + } + @Override public Request personalSendTransaction( Transaction transaction, String passphrase) { @@ -88,5 +163,14 @@ public Request personalSendTransaction( web3jService, PlatonSendTransaction.class); } - + + @Override + public Request txPoolContent() { + return new Request<>( + "txpool_content", + Collections.emptyList(), + web3jService, + TxPoolContent.class); + } + } diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalEcRecover.java b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalEcRecover.java new file mode 100644 index 00000000..25769b5c --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalEcRecover.java @@ -0,0 +1,11 @@ +package com.platon.protocol.admin.methods.response; + + +import com.platon.protocol.core.Response; + +/** personal_ecRecover. */ +public class PersonalEcRecover extends Response { + public String getRecoverAccountId() { + return getResult(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalImportRawKey.java b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalImportRawKey.java new file mode 100644 index 00000000..121315f3 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalImportRawKey.java @@ -0,0 +1,11 @@ +package com.platon.protocol.admin.methods.response; + + +import com.platon.protocol.core.Response; + +/** personal_importRawKey. */ +public class PersonalImportRawKey extends Response { + public String getAccountId() { + return getResult(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalListWallets.java b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalListWallets.java new file mode 100644 index 00000000..695d6ed9 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/PersonalListWallets.java @@ -0,0 +1,63 @@ +package com.platon.protocol.admin.methods.response; + +import com.platon.protocol.core.Response; + +import java.util.List; + +/** personal_listWallets **/ +public class PersonalListWallets extends Response> { + + + public static class Wallet { + private List accounts; + private String status; + private String url; + + public List getAccounts() { + return accounts; + } + + public void setAccounts(List accounts) { + this.accounts = accounts; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + } + + public static class Account { + private String url; + private String address; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolContent.java b/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolContent.java new file mode 100644 index 00000000..18f7dc55 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolContent.java @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.platon.protocol.admin.methods.response; + + +import com.platon.protocol.core.Response; +import com.platon.protocol.core.methods.response.Transaction; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** txpool_content */ +public class TxPoolContent extends Response { + public static class TxPoolContentResult { + + private Map> pending; + private Map> queued; + + public TxPoolContentResult() {} + + public TxPoolContentResult( + Map> pending, + Map> queued) { + this.pending = + Collections.unmodifiableMap( + pending.entrySet().stream() + .collect( + Collectors.toMap( + (Map.Entry> + i) -> i.getKey().toLowerCase(), + (Map.Entry> + i) -> + Collections.unmodifiableMap( + i.getValue())))); + this.queued = + Collections.unmodifiableMap( + queued.entrySet().stream() + .collect( + Collectors.toMap( + (Map.Entry> + i) -> i.getKey().toLowerCase(), + (Map.Entry> + i) -> + Collections.unmodifiableMap( + i.getValue())))); + } + + public Map> getPending() { + return pending; + } + + public Map> getQueued() { + return queued; + } + + public List getPendingTransactions() { + return pending.values().stream() + .flatMap(i -> i.values().stream()) + .collect(Collectors.toList()); + } + + public List getQueuedTransactions() { + return queued.values().stream() + .flatMap(i -> i.values().stream()) + .collect(Collectors.toList()); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolStatus.java b/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolStatus.java new file mode 100644 index 00000000..216bacfb --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/TxPoolStatus.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.platon.protocol.admin.methods.response; + + +import com.platon.protocol.core.Response; +import com.platon.utils.Numeric; + +import java.util.Map; + +/** txpool_status. */ +public class TxPoolStatus extends Response> { + public Integer getPending() { + return Numeric.decodeQuantity((String) getResult().get("pending")).intValue(); + } + + public Integer getQueued() { + return Numeric.decodeQuantity((String) getResult().get("queued")).intValue(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/admin/methods/response/admin/AdminDataDir.java b/core/src/main/java/com/platon/protocol/admin/methods/response/admin/AdminDataDir.java new file mode 100644 index 00000000..57c19fbb --- /dev/null +++ b/core/src/main/java/com/platon/protocol/admin/methods/response/admin/AdminDataDir.java @@ -0,0 +1,23 @@ +/* + * Copyright 2019 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.platon.protocol.admin.methods.response.admin; + +import com.platon.protocol.core.Response; + +/** String response type. */ +public class AdminDataDir extends Response { + public String getDataDir() { + return getResult(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/JsonRpc2_0Web3j.java b/core/src/main/java/com/platon/protocol/core/JsonRpc2_0Web3j.java index 4aa436b3..f386b3fd 100644 --- a/core/src/main/java/com/platon/protocol/core/JsonRpc2_0Web3j.java +++ b/core/src/main/java/com/platon/protocol/core/JsonRpc2_0Web3j.java @@ -2,6 +2,11 @@ import com.platon.protocol.Web3j; import com.platon.protocol.Web3jService; +import com.platon.protocol.admin.methods.response.BooleanResponse; +import com.platon.protocol.admin.methods.response.TxPoolStatus; +import com.platon.protocol.admin.methods.response.admin.AdminDataDir; +import com.platon.protocol.core.methods.response.DebugWaitSlashingNodeList; +import com.platon.protocol.core.methods.response.PlatonSignTransaction; import com.platon.protocol.core.methods.request.ShhFilter; import com.platon.protocol.core.methods.response.*; import com.platon.protocol.rx.JsonRpc2_0Rx; @@ -9,6 +14,7 @@ import com.platon.protocol.websocket.events.NewHeadsNotification; import com.platon.utils.Async; import com.platon.utils.Numeric; +import com.platon.utils.Strings; import rx.Observable; import java.io.IOException; @@ -86,6 +92,90 @@ public Request netPeerCount() { NetPeerCount.class); } + @Override + public Request adminNodeInfo() { + return new Request<>( + "admin_nodeInfo", Collections.emptyList(), web3jService, AdminNodeInfo.class); + } + + @Override + public Request adminPeers() { + return new Request<>( + "admin_peers", Collections.emptyList(), web3jService, AdminPeers.class); + } + + @Override + public Request adminAddPeer(String url) { + return new Request<>( + "admin_addPeer", Arrays.asList(url), web3jService, BooleanResponse.class); + } + + @Override + public Request adminRemovePeer(String url) { + return new Request<>( + "admin_removePeer", Arrays.asList(url), web3jService, BooleanResponse.class); + } + + @Override + public Request adminDataDir() { + return new Request<>( + "admin_datadir", Collections.emptyList(), web3jService, AdminDataDir.class); + } + + @Override + public Request adminStartRPC(String host,int port,String cors,String apis) { + if(host == null){ + host = "localhost"; + } + if(cors == null){ + cors = ""; + } + if(Strings.isBlank(apis)){ + apis = "platon,net,web3,debug,admin"; + } + return new Request<>( + "admin_startRPC", Arrays.asList(host,port,cors,apis), web3jService, BooleanResponse.class); + } + + @Override + public Request adminStartWS(String host,int port,String cors,String apis) { + if(host == null){ + host = "localhost"; + } + if(cors == null){ + cors = ""; + } + if(Strings.isBlank(apis)){ + apis = "platon,net,web3,debug,admin"; + } + return new Request<>( + "admin_startWS", Arrays.asList(host,port,cors,apis), web3jService, BooleanResponse.class); + } + + @Override + public Request adminStopRPC() { + return new Request<>( + "admin_stopRPC", Collections.emptyList(), web3jService, BooleanResponse.class); + } + + @Override + public Request adminStopWS() { + return new Request<>( + "admin_stopWS", Collections.emptyList(), web3jService, BooleanResponse.class); + } + + @Override + public Request adminExportChain(String file) { + return new Request<>( + "admin_exportChain", Arrays.asList(file), web3jService, BooleanResponse.class); + } + + @Override + public Request adminImportChain(String file) { + return new Request<>( + "admin_importChain", Arrays.asList(file), web3jService, BooleanResponse.class); + } + @Override public Request platonProtocolVersion() { return new Request<>( @@ -514,6 +604,12 @@ public Request shhGetMessages(BigInteger filterId) { ShhMessages.class); } + @Override + public Request txPoolStatus() { + return new Request<>( + "txpool_status", Collections.emptyList(), web3jService, TxPoolStatus.class); + } + @Override public Observable newHeadsNotifications() { return web3jService.subscribe( @@ -664,32 +760,32 @@ public Request platonEvidences() { PlatonEvidences.class); } - @Override - public Request getProgramVersion() { + @Override + public Request getProgramVersion() { return new Request<>( "admin_getProgramVersion", Collections.emptyList(), web3jService, AdminProgramVersion.class); - } + } - @Override - public Request getSchnorrNIZKProve() { + @Override + public Request getSchnorrNIZKProve() { return new Request<>( "admin_getSchnorrNIZKProve", Collections.emptyList(), web3jService, AdminSchnorrNIZKProve.class); - } + } - @Override - public Request getEconomicConfig() { + @Override + public Request getEconomicConfig() { return new Request<>( "debug_economicConfig", Collections.emptyList(), web3jService, DebugEconomicConfig.class); - } + } @Override public Request getChainId() { @@ -699,4 +795,76 @@ public Request getChainId() { web3jService, PlatonChainId.class); } + + @Override + public Request getWaitSlashingNodeList() { + return new Request<>( + "debug_getWaitSlashingNodeList", + Collections.emptyList(), + web3jService, + DebugWaitSlashingNodeList.class); + } + + @Override + public Request platonGetRawTransactionByHash(String transactionHash) { + return new Request<>( + "platon_getRawTransactionByHash", + Arrays.asList(transactionHash), + web3jService, + PlatonRawTransaction.class); + } + + @Override + public Request platonGetRawTransactionByBlockHashAndIndex(String blockHash, String index) { + return new Request<>( + "platon_getRawTransactionByBlockHashAndIndex", + Arrays.asList(blockHash,index), + web3jService, + PlatonRawTransaction.class); + } + + @Override + public Request platonGetRawTransactionByBlockNumberAndIndex(String blockNumber, String index) { + return new Request<>( + "platon_getRawTransactionByBlockNumberAndIndex", + Arrays.asList(blockNumber,index), + web3jService, + PlatonRawTransaction.class); + } + + @Override + public Request platonGetAddressHrp() { + return new Request<>( + "platon_getAddressHrp", + Collections.emptyList(), + web3jService, + PlatonGetAddressHrp.class); + } + + @Override + public Request platonSignTransaction(com.platon.protocol.core.methods.request.Transaction transaction) { + return new Request<>( + "platon_signTransaction", + Arrays.asList(transaction), + web3jService, + PlatonSignTransaction.class); + } + + @Override + public Request minerSetGasPrice(String minGasPrice) { + return new Request<>( + "miner_setGasPrice", + Arrays.asList(minGasPrice), + web3jService, + BooleanResponse.class); + } + + @Override + public Request adminPeerEvents() { + return new Request<>( + "admin_peerEvents", + Collections.emptyList(), + web3jService, + AdminPeerEvents.class); + } } diff --git a/core/src/main/java/com/platon/protocol/core/Platon.java b/core/src/main/java/com/platon/protocol/core/Platon.java index 485986b4..d27c5fe2 100644 --- a/core/src/main/java/com/platon/protocol/core/Platon.java +++ b/core/src/main/java/com/platon/protocol/core/Platon.java @@ -1,5 +1,10 @@ package com.platon.protocol.core; +import com.platon.protocol.admin.methods.response.BooleanResponse; +import com.platon.protocol.admin.methods.response.TxPoolStatus; +import com.platon.protocol.admin.methods.response.admin.AdminDataDir; +import com.platon.protocol.core.methods.response.DebugWaitSlashingNodeList; +import com.platon.protocol.core.methods.response.PlatonSignTransaction; import com.platon.protocol.core.methods.request.ShhFilter; import com.platon.protocol.core.methods.response.*; @@ -19,6 +24,28 @@ public interface Platon { Request netPeerCount(); + Request adminNodeInfo(); + + Request adminPeers(); + + Request adminAddPeer(String url); + + Request adminRemovePeer(String url); + + Request adminDataDir(); + + Request adminStartRPC(String host,int port,String cors,String apis); + + Request adminStartWS(String host,int port,String cors,String apis); + + Request adminStopRPC(); + + Request adminStopWS(); + + Request adminExportChain(String file); + + Request adminImportChain(String file); + Request platonProtocolVersion(); Request platonSyncing(); @@ -123,13 +150,31 @@ Request shhPost( Request shhGetMessages(BigInteger filterId); + Request txPoolStatus(); + Request platonEvidences(); - + Request getProgramVersion(); - + Request getSchnorrNIZKProve(); - + Request getEconomicConfig(); Request getChainId(); + + Request getWaitSlashingNodeList(); + + Request platonGetRawTransactionByHash(String transactionHash); + + Request platonGetRawTransactionByBlockHashAndIndex(String blockHash, String index); + + Request platonGetRawTransactionByBlockNumberAndIndex(String blockNumber, String index); + + Request platonGetAddressHrp(); + + Request platonSignTransaction(com.platon.protocol.core.methods.request.Transaction transaction); + + Request minerSetGasPrice(String minGasPrice); + + Request adminPeerEvents(); } diff --git a/core/src/main/java/com/platon/protocol/core/Request.java b/core/src/main/java/com/platon/protocol/core/Request.java index 87f0713a..c256ef6d 100644 --- a/core/src/main/java/com/platon/protocol/core/Request.java +++ b/core/src/main/java/com/platon/protocol/core/Request.java @@ -12,6 +12,7 @@ public class Request { private static AtomicLong nextId = new AtomicLong(0); private String jsonrpc = "2.0"; + private boolean bech32 = true; //bech32的值来区分是以太坊/PlatON调用 private String method; private List params; private long id; @@ -42,6 +43,14 @@ public void setJsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; } + public boolean getBech32() { + return bech32; + } + + public void setBech32(boolean bech32) { + this.bech32 = bech32; + } + public String getMethod() { return method; } diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/AdminNodeInfo.java b/core/src/main/java/com/platon/protocol/core/methods/response/AdminNodeInfo.java new file mode 100644 index 00000000..3b04368d --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/AdminNodeInfo.java @@ -0,0 +1,107 @@ +package com.platon.protocol.core.methods.response; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.platon.protocol.ObjectMapperFactory; +import com.platon.protocol.core.Response; + + +/** admin_nodeInfo. */ +public class AdminNodeInfo extends Response { + @JsonIgnoreProperties(ignoreUnknown = true) + @Override + @JsonDeserialize(using = AdminNodeInfo.ResponseDeserialiser.class) + public void setResult(NodeInfo result) { + super.setResult(result); + } + + public static class NodeInfo { + private String enode; + private String id; + private String ip; + private String listenAddr; + private String name; + private String consensus; + + @JsonProperty("protocols") + @SuppressWarnings("unchecked") + private void consensusDeserializer(Map protocols) { + if (protocols.containsKey("istanbul")) { + consensus = "istanbul"; + } else if (protocols.containsKey("clique")) { + consensus = "clique"; + } else if (protocols.containsKey("eth")) { + Map eth = (Map) protocols.get("eth"); + consensus = (String) eth.get("consensus"); + } else { + consensus = "unknown"; + } + } + + public NodeInfo() {} + + public NodeInfo( + String enode, + String id, + String ip, + String listenAddr, + String name, + String consensus) { + this.enode = enode; + this.id = id; + this.ip = ip; + this.listenAddr = listenAddr; + this.name = name; + this.consensus = consensus; + } + + public String getEnode() { + return enode; + } + + public String getId() { + return id; + } + + public String getIp() { + return ip; + } + + public String getListenAddr() { + return listenAddr; + } + + public String getName() { + return name; + } + + public String getConsensus() { + return consensus; + } + } + + public static class ResponseDeserialiser extends JsonDeserializer { + + private ObjectReader objectReader = ObjectMapperFactory.getObjectReader(); + + @Override + public NodeInfo deserialize( + JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + if (jsonParser.getCurrentToken() != JsonToken.VALUE_NULL) { + return objectReader.readValue(jsonParser, NodeInfo.class); + } else { + return null; // null is wrapped by Optional in above getter + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeerEvents.java b/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeerEvents.java new file mode 100644 index 00000000..9e11df40 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeerEvents.java @@ -0,0 +1,12 @@ +package com.platon.protocol.core.methods.response; + +import com.platon.protocol.core.Response; + +/** + * @Author liushuyu + * @Date 2021/9/16 11:37 + * @Version + * @Desc + */ +public class AdminPeerEvents extends Response { +} diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeers.java b/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeers.java new file mode 100644 index 00000000..b4b3e329 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/AdminPeers.java @@ -0,0 +1,95 @@ +package com.platon.protocol.core.methods.response; + +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.platon.protocol.ObjectMapperFactory; +import com.platon.protocol.core.Response; + + +/** admin_peers. */ +public class AdminPeers extends Response> { + @JsonIgnoreProperties(ignoreUnknown = true) + @Override + @JsonDeserialize(using = AdminPeers.ResponseDeserializer.class) + public void setResult(List result) { + super.setResult(result); + } + + public static class Peer { + public Peer() {} + + public Peer(String id, String name, String enode, PeerNetwork network) { + this.id = id; + this.name = name; + this.network = network; + this.enode = enode; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEnode() { + return enode; + } + + private String id; + private String name; + private PeerNetwork network; + private String enode; + + public PeerNetwork getNetwork() { + return network; + } + } + + public static class PeerNetwork { + + public PeerNetwork() {} + + private String localAddress; + private String remoteAddress; + + public PeerNetwork(String localAddress, String remoteAddress) { + this.localAddress = localAddress; + this.remoteAddress = remoteAddress; + } + + public String getLocalAddress() { + return localAddress; + } + + public String getRemoteAddress() { + return remoteAddress; + } + } + + public static class ResponseDeserializer extends JsonDeserializer> { + + private ObjectReader objectReader = ObjectMapperFactory.getObjectReader(); + + @Override + public List deserialize( + JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + if (jsonParser.getCurrentToken() != JsonToken.VALUE_NULL) { + return objectReader.readValue(jsonParser, new TypeReference>() {}); + } else { + return null; // null is wrapped by Optional in above getter + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/DebugWaitSlashingNodeList.java b/core/src/main/java/com/platon/protocol/core/methods/response/DebugWaitSlashingNodeList.java new file mode 100644 index 00000000..db46730d --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/DebugWaitSlashingNodeList.java @@ -0,0 +1,14 @@ +package com.platon.protocol.core.methods.response; + +import com.platon.protocol.core.Response; +import com.platon.protocol.core.methods.response.bean.WaitSlashingNode; +import com.platon.utils.JSONUtil; +import java.util.List; +/** + * debug_getWaitSlashingNodeList + */ +public class DebugWaitSlashingNodeList extends Response { + public List get() { + return JSONUtil.parseArray(getResult(), WaitSlashingNode.class); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/PlatonGetAddressHrp.java b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonGetAddressHrp.java new file mode 100644 index 00000000..2fa79e22 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonGetAddressHrp.java @@ -0,0 +1,7 @@ +package com.platon.protocol.core.methods.response; + +import com.platon.protocol.core.Response; + + +public class PlatonGetAddressHrp extends Response { +} diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/PlatonRawTransaction.java b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonRawTransaction.java new file mode 100644 index 00000000..aee0d815 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonRawTransaction.java @@ -0,0 +1,8 @@ +package com.platon.protocol.core.methods.response; + + +import com.platon.protocol.core.Response; + +/** platon_getRawTransactionByHash **/ +public class PlatonRawTransaction extends Response { +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/PlatonSignTransaction.java b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonSignTransaction.java new file mode 100644 index 00000000..7e9e16b6 --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/PlatonSignTransaction.java @@ -0,0 +1,33 @@ +package com.platon.protocol.core.methods.response; + +import com.platon.protocol.core.Response; + +/** + * @Author liushuyu + * @Date 2021/9/16 11:21 + * @Version + * @Desc + */ +public class PlatonSignTransaction extends Response { + + public static class SignTransaction { + private String raw; + private Transaction tx; + + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + public Transaction getTx() { + return tx; + } + + public void setTx(Transaction tx) { + this.tx = tx; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/core/methods/response/bean/WaitSlashingNode.java b/core/src/main/java/com/platon/protocol/core/methods/response/bean/WaitSlashingNode.java new file mode 100644 index 00000000..b2dfd45a --- /dev/null +++ b/core/src/main/java/com/platon/protocol/core/methods/response/bean/WaitSlashingNode.java @@ -0,0 +1,53 @@ +package com.platon.protocol.core.methods.response.bean; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigInteger; + +/** + * @Author liushuyu + * @Date 2021/7/6 14:38 + * @Version + * @Desc + */ +public class WaitSlashingNode { + + //零出块的节点ID + @JsonProperty("NodeId") + private String nodeId; + //观察期内第一次零出块时所处共识轮数 + @JsonProperty("Round") + private BigInteger round; + //零出块次数位图(从Round开始,1表示该轮未出块) + @JsonProperty("CountBit") + private BigInteger countBit; + public String getNodeId() { + return nodeId; + } + @JsonIgnore + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + public BigInteger getRound() { + return round; + } + @JsonIgnore + public void setRound(BigInteger round) { + this.round = round; + } + public BigInteger getCountBit() { + return countBit; + } + @JsonIgnore + public void setCountBit(BigInteger countBit) { + this.countBit = countBit; + } + @Override + public String toString() { + return "WaitSlashingNode{" + + "nodeId='" + nodeId + '\'' + + ", round=" + round + + ", countBit=" + countBit + + '}'; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/protocol/websocket/WebSocketService.java b/core/src/main/java/com/platon/protocol/websocket/WebSocketService.java index 6c67816b..dc19bc59 100644 --- a/core/src/main/java/com/platon/protocol/websocket/WebSocketService.java +++ b/core/src/main/java/com/platon/protocol/websocket/WebSocketService.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; @@ -62,7 +63,7 @@ public WebSocketService(String serverUrl, boolean includeRawResponses) { } public WebSocketService(WebSocketClient webSocketClient, - boolean includeRawResponses) { + boolean includeRawResponses) { this(webSocketClient, Executors.newScheduledThreadPool(1), includeRawResponses); } @@ -159,9 +160,9 @@ private void sendRequest(Request request, long requestId) throws JsonProcessingE private void setRequestTimeout(long requestId) { executor.schedule( () -> closeRequest( - requestId, - new IOException( - String.format("Request with id %d timed out", requestId))), + requestId, + new IOException( + String.format("Request with id %d timed out", requestId))), REQUEST_TIMEOUT, TimeUnit.SECONDS); } @@ -402,10 +403,10 @@ private void unsubscribeFromEventsStream(String subscriptionId, String unsubscri private Request unsubscribeRequest( String subscriptionId, String unsubscribeMethod) { return new Request<>( - unsubscribeMethod, - Collections.singletonList(subscriptionId), - this, - PlatonUnsubscribe.class); + unsubscribeMethod, + Collections.singletonList(subscriptionId), + this, + PlatonUnsubscribe.class); } @Override diff --git a/core/src/main/java/com/platon/tx/Contract.java b/core/src/main/java/com/platon/tx/Contract.java index a33a6cd7..b11f64b1 100644 --- a/core/src/main/java/com/platon/tx/Contract.java +++ b/core/src/main/java/com/platon/tx/Contract.java @@ -1,5 +1,4 @@ package com.platon.tx; - import com.platon.abi.solidity.*; import com.platon.abi.solidity.datatypes.Address; import com.platon.abi.solidity.datatypes.Event; @@ -10,6 +9,7 @@ import com.platon.protocol.core.DefaultBlockParameter; import com.platon.protocol.core.DefaultBlockParameterName; import com.platon.protocol.core.RemoteCall; +import com.platon.protocol.core.Response; import com.platon.protocol.core.methods.request.Transaction; import com.platon.protocol.core.methods.response.Log; import com.platon.protocol.core.methods.response.PlatonCall; @@ -17,9 +17,12 @@ import com.platon.protocol.core.methods.response.TransactionReceipt; import com.platon.protocol.exceptions.TransactionException; import com.platon.tx.exceptions.ContractCallException; +import com.platon.tx.exceptions.PlatonCallException; +import com.platon.tx.exceptions.PlatonCallTimeoutException; import com.platon.tx.gas.DefaultGasProvider; import com.platon.tx.gas.GasProvider; import com.platon.utils.Numeric; +import com.platon.utils.Strings; import java.io.IOException; import java.lang.reflect.Constructor; @@ -149,6 +152,20 @@ private List executeCall( defaultBlockParameter) .send(); + //判断底层返回的错误信息是否包含超时信息 + if(ethCall.hasError()){ + Response.Error error = ethCall.getError(); + String message = error.getMessage(); + String lowMessage = !Strings.isBlank(message)? message.toLowerCase() : null; + //包含timeout则抛超时异常,其他错误则直接抛出runtime异常 + if(!Strings.isBlank(lowMessage) + && lowMessage.contains("timeout")){ + throw new PlatonCallTimeoutException(error.getCode(),error.getMessage(),ethCall); + } else { + throw new PlatonCallException(error.getCode(),error.getMessage(),ethCall); + } + } + String value = ethCall.getValue(); return FunctionReturnDecoder.decode(value, function.getOutputParameters()); } @@ -205,7 +222,7 @@ private TransactionReceipt executeTransaction( * Given the duration required to execute a transaction. * * @param data to send in transaction - * @param vonValue in Von to send in transaction + * @param weiValue in Von to send in transaction * @return {@link Optional} containing our transaction receipt * @throws IOException if the call to the node fails * @throws TransactionException if the transaction was not mined while waiting @@ -268,7 +285,7 @@ private static T create( return contract; } - protected static T deploy(Class type, Web3j web3j, Credentials credentials, GasProvider contractGasProvider, String binary, String encodedConstructor, BigInteger value) throws RuntimeException, TransactionException { + protected static T deploy(Class type, Web3j web3j, Credentials credentials, GasProvider contractGasProvider, String binary, String encodedConstructor, BigInteger value) throws RuntimeException, TransactionException, IOException { try { Constructor constructor = type.getDeclaredConstructor(String.class, Web3j.class, Credentials.class, GasProvider.class); @@ -280,12 +297,14 @@ protected static T deploy(Class type, Web3j web3j, Crede return create(contract, binary, encodedConstructor, value); } catch (TransactionException e) { throw e; + } catch (IOException e) { + throw e; } catch (Exception e) { throw new RuntimeException(e); } } - protected static T deploy(Class type, Web3j web3j, TransactionManager transactionManager, GasProvider contractGasProvider, String binary, String encodedConstructor, BigInteger value) throws RuntimeException, TransactionException { + protected static T deploy(Class type, Web3j web3j, TransactionManager transactionManager, GasProvider contractGasProvider, String binary, String encodedConstructor, BigInteger value) throws RuntimeException, TransactionException, IOException{ try { Constructor constructor = type.getDeclaredConstructor(String.class, Web3j.class, TransactionManager.class, GasProvider.class); @@ -296,6 +315,8 @@ protected static T deploy(Class type, Web3j web3j, Trans return create(contract, binary, encodedConstructor, value); } catch (TransactionException e) { throw e; + } catch (IOException e) { + throw e; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/core/src/main/java/com/platon/tx/exceptions/PlatonCallException.java b/core/src/main/java/com/platon/tx/exceptions/PlatonCallException.java new file mode 100644 index 00000000..a8e2f01a --- /dev/null +++ b/core/src/main/java/com/platon/tx/exceptions/PlatonCallException.java @@ -0,0 +1,44 @@ +package com.platon.tx.exceptions; + +import com.platon.protocol.core.Response; + +import java.io.IOException; + +/** + * @Author liushuyu + * @Date 2021/5/12 16:46 + * @Version + * @Desc + */ +public class PlatonCallException extends RuntimeException { + + private Response response; + private int code; + private String msg; + + public PlatonCallException(int code, String msg, Response response){ + this.code = code; + this.msg = msg; + this.response = response; + } + + public PlatonCallException(int code, String msg, Response response, Throwable ex){ + super(ex); + this.code = code; + this.msg = msg; + this.response = response; + } + + public Response getResponse() { + return response; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/platon/tx/exceptions/PlatonCallTimeoutException.java b/core/src/main/java/com/platon/tx/exceptions/PlatonCallTimeoutException.java new file mode 100644 index 00000000..745e10e3 --- /dev/null +++ b/core/src/main/java/com/platon/tx/exceptions/PlatonCallTimeoutException.java @@ -0,0 +1,44 @@ +package com.platon.tx.exceptions; + +import com.platon.protocol.core.Response; + +import java.io.IOException; + +/** + * @Author liushuyu + * @Date 2021/5/12 16:46 + * @Version + * @Desc + */ +public class PlatonCallTimeoutException extends IOException { + + private Response response; + private int code; + private String msg; + + public PlatonCallTimeoutException(int code,String msg,Response response){ + this.code = code; + this.msg = msg; + this.response = response; + } + + public PlatonCallTimeoutException(int code,String msg,Response response,Throwable ex){ + super(ex); + this.code = code; + this.msg = msg; + this.response = response; + } + + public Response getResponse() { + return response; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/platon/protocol/admin/RequestTest.java b/core/src/test/java/com/platon/protocol/admin/RequestTest.java index ecac40ac..d7292a39 100644 --- a/core/src/test/java/com/platon/protocol/admin/RequestTest.java +++ b/core/src/test/java/com/platon/protocol/admin/RequestTest.java @@ -20,7 +20,7 @@ protected void initWeb3Client(HttpService httpService) { public void testPersonalListAccounts() throws Exception { web3j.personalListAccounts().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"personal_listAccounts\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"personal_listAccounts\"," + "\"params\":[],\"id\":1}"); } @@ -28,7 +28,7 @@ public void testPersonalListAccounts() throws Exception { public void testPersonalNewAccount() throws Exception { web3j.personalNewAccount("password").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"personal_newAccount\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"personal_newAccount\"," + "\"params\":[\"password\"],\"id\":1}"); } @@ -48,7 +48,7 @@ public void testPersonalSendTransaction() throws Exception { ).send(); //CHECKSTYLE:OFF - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"personal_sendTransaction\",\"params\":[{\"from\":\"FROM\",\"to\":\"TO\",\"gas\":\"0x1\",\"gasPrice\":\"0xa\",\"value\":\"0x0\",\"data\":\"0xDATA\",\"nonce\":\"0x1\"},\"password\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"personal_sendTransaction\",\"params\":[{\"from\":\"FROM\",\"to\":\"TO\",\"gas\":\"0x1\",\"gasPrice\":\"0xa\",\"value\":\"0x0\",\"data\":\"0xDATA\",\"nonce\":\"0x1\"},\"password\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -57,7 +57,7 @@ public void testPersonalUnlockAccount() throws Exception { web3j.personalUnlockAccount( "0xfc390d8a8ddb591b010fda52f4db4945742c3809", "hunter2", BigInteger.ONE).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"personal_unlockAccount\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"personal_unlockAccount\"," + "\"params\":[\"0xfc390d8a8ddb591b010fda52f4db4945742c3809\",\"hunter2\",1]," + "\"id\":1}"); } @@ -66,8 +66,16 @@ public void testPersonalUnlockAccount() throws Exception { public void testPersonalUnlockAccountNoDuration() throws Exception { web3j.personalUnlockAccount("0xfc390d8a8ddb591b010fda52f4db4945742c3809", "hunter2").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"personal_unlockAccount\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"personal_unlockAccount\"," + "\"params\":[\"0xfc390d8a8ddb591b010fda52f4db4945742c3809\",\"hunter2\",null]," + "\"id\":1}"); } + + @Test + public void testTxPoolContent() throws Exception { + web3j.txPoolContent().send(); + + verifyResult( + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"txpool_content\"," + "\"params\":[],\"id\":1}"); + } } diff --git a/core/src/test/java/com/platon/protocol/admin/ResponseTest.java b/core/src/test/java/com/platon/protocol/admin/ResponseTest.java index 9bc6caef..9d31f76c 100644 --- a/core/src/test/java/com/platon/protocol/admin/ResponseTest.java +++ b/core/src/test/java/com/platon/protocol/admin/ResponseTest.java @@ -8,8 +8,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Parity/Geth Shared Protocol Response tests. @@ -90,5 +89,65 @@ public void testPersonalUnlockAccount() { deserialiseResponse(PersonalUnlockAccount.class); assertTrue(personalUnlockAccount.accountUnlocked()); } - + + @Test + public void testTxPoolContent() { + buildResponse( + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"id\": 1,\n" + + " \"result\": {\n" + + " \"pending\": {\n" + + " \"0x0032D05F320fa74C871E892F48F0e6387c0Dfe95\": {\n" + + " \"0\": {\n" + + " \"blockHash\": null,\n" + + " \"blockNumber\": null,\n" + + " \"from\": \"0x0032d05f320fa74c871e892f48f0e6387c0dfe95\",\n" + + " \"gas\": \"0x63cad\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"hash\": \"0x56cf53cbd377535c14b28cd373fa43d129f501b1a20b36903fd14b747c3f6cf5\",\n" + + " \"input\": \"0x608060405234801561001057600080fd5b5060405161060a38038061060a833981018060405281019080805190602001909291908051820192919060200180519060200190929190805190602001909291908051906020019092919050505084848160008173ffffffffffffffffffffffffffffffffffffffff1614151515610116576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f496e76616c6964206d617374657220636f707920616464726573732070726f7681526020017f696465640000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506000815111156101a35773ffffffffffffffffffffffffffffffffffffffff60005416600080835160208501846127105a03f46040513d6000823e600082141561019f573d81fd5b5050505b5050600081111561036d57600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156102b7578273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015156102b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001807f436f756c64206e6f74207061792073616665206372656174696f6e207769746881526020017f206574686572000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b61036c565b6102d1828483610377640100000000026401000000009004565b151561036b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001807f436f756c64206e6f74207061792073616665206372656174696f6e207769746881526020017f20746f6b656e000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b5b5b5050505050610490565b600060608383604051602401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000808251602084016000896127105a03f16040513d6000823e3d60008114610473576020811461047b5760009450610485565b829450610485565b8151158315171594505b505050509392505050565b61016b8061049f6000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634555d5c91461008b5780635c60da1b146100b6575b73ffffffffffffffffffffffffffffffffffffffff600054163660008037600080366000845af43d6000803e6000811415610086573d6000fd5b3d6000f35b34801561009757600080fd5b506100a061010d565b6040518082815260200191505060405180910390f35b3480156100c257600080fd5b506100cb610116565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60006002905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050905600a165627a7a7230582007fffd557dfc8c4d2fdf56ba6381a6ce5b65b6260e1492d87f26c6d4f1d0410800290000000000000000000000008942595a2dc5181df0465af0d7be08c8f23c93af00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000004ba9692da667218aa968ced8cbe59fe193e0d7860000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006968500000000000000000000000000000000000000000000000000000000000001240ec78d9e00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000006ea3e1c44f2ad3e54cf32a25eb9fab965fc010f0000000000000000000000009542597a73c7371f07b4532bda22d39cfc4912180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\n" + + " \"nonce\": \"0x0\",\n" + + " \"to\": null,\n" + + " \"transactionIndex\": null,\n" + + " \"value\": \"0x0\",\n" + + " \"v\": \"0x1b\",\n" + + " \"r\": \"0x1b53058de9ed675d8b6583c559d23013e941905dad28dfd95ba3ff4f38ace0\",\n" + + " \"s\": \"0x8631cfd224ee69034f8040d2297a28229ee91cd5acdabc7f1de5be62220\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"queued\": {\n" + + " \"0x00Bf700CeB382877F8bFa38b05fcC81126f4f228\": {\n" + + " \"49\": {\n" + + " \"blockHash\": null,\n" + + " \"blockNumber\": null,\n" + + " \"from\": \"0x00bf700ceb382877f8bfa38b05fcc81126f4f228\",\n" + + " \"gas\": \"0xfa00\",\n" + + " \"gasPrice\": \"0x3b9aca00\",\n" + + " \"hash\": \"0xa87ab980c4d277de6c4faf2670a2ec1b6e577482e582c8082d208b7e630cf395\",\n" + + " \"input\": \"0xa6ab36f2000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000009590f23a286dade6fbf778ca651ad560d4e02fdc\",\n" + + " \"nonce\": \"0x31\",\n" + + " \"to\": \"0x974d7219184a41d4f5e3664ddce808c7853d3ab4\",\n" + + " \"transactionIndex\": null,\n" + + " \"value\": \"0x0\",\n" + + " \"v\": \"0x2b\",\n" + + " \"r\": \"0xc747162f13dd24fd03dd00c66c5c3d222595908319333c2b08e7f7def8dad2e7\",\n" + + " \"s\": \"0x8e6a2c4e3794d782d51f950e0ba546f44412e8c2409bd39149d78d7ca316e8a\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + + TxPoolContent content = deserialiseResponse(TxPoolContent.class); + + assertEquals( + content.getResult().getPendingTransactions().get(0).getFrom(), + "0x0032d05f320fa74c871e892f48f0e6387c0dfe95"); + assertEquals( + content.getResult().getQueuedTransactions().get(0).getFrom(), + "0x00bf700ceb382877f8bfa38b05fcc81126f4f228"); + } + } diff --git a/core/src/test/java/com/platon/protocol/core/RequestTest.java b/core/src/test/java/com/platon/protocol/core/RequestTest.java index d55e4074..1c89bcec 100644 --- a/core/src/test/java/com/platon/protocol/core/RequestTest.java +++ b/core/src/test/java/com/platon/protocol/core/RequestTest.java @@ -27,7 +27,7 @@ public void testWeb3ClientVersion() throws Exception { web3j.web3ClientVersion().send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"web3_clientVersion\",\"params\":[],\"id\":1}"); + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"web3_clientVersion\",\"params\":[],\"id\":1}"); } @Test @@ -35,7 +35,7 @@ public void testWeb3Sha3() throws Exception { web3j.web3Sha3("0x68656c6c6f20776f726c64").send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"web3_sha3\"," + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"web3_sha3\"," + "\"params\":[\"0x68656c6c6f20776f726c64\"],\"id\":1}"); } @@ -43,21 +43,21 @@ public void testWeb3Sha3() throws Exception { public void testNetVersion() throws Exception { web3j.netVersion().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"net_version\",\"params\":[],\"id\":1}"); } @Test public void testNetListening() throws Exception { web3j.netListening().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"net_listening\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"net_listening\",\"params\":[],\"id\":1}"); } @Test public void testNetPeerCount() throws Exception { web3j.netPeerCount().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"net_peerCount\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"net_peerCount\",\"params\":[],\"id\":1}"); } @Test @@ -65,35 +65,35 @@ public void testEthProtocolVersion() throws Exception { web3j.platonProtocolVersion().send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"platon_protocolVersion\",\"params\":[],\"id\":1}"); + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_protocolVersion\",\"params\":[],\"id\":1}"); } @Test public void testEthSyncing() throws Exception { web3j.platonSyncing().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_syncing\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_syncing\",\"params\":[],\"id\":1}"); } @Test public void testEthGasPrice() throws Exception { web3j.platonGasPrice().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_gasPrice\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_gasPrice\",\"params\":[],\"id\":1}"); } @Test public void testEthAccounts() throws Exception { web3j.platonAccounts().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_accounts\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_accounts\",\"params\":[],\"id\":1}"); } @Test public void testEthBlockNumber() throws Exception { web3j.platonBlockNumber().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_blockNumber\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_blockNumber\",\"params\":[],\"id\":1}"); } @Test @@ -102,7 +102,7 @@ public void testEthGetBalance() throws Exception { DefaultBlockParameterName.LATEST).send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"platon_getBalance\"," + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getBalance\"," + "\"params\":[\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\"latest\"]," + "\"id\":1}"); } @@ -112,7 +112,7 @@ public void testEthGetStorageAt() throws Exception { web3j.platonGetStorageAt("0x295a70b2de5e3953354a6a8344e616ed314d7251", BigInteger.ZERO, DefaultBlockParameterName.LATEST).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getStorageAt\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getStorageAt\"," + "\"params\":[\"0x295a70b2de5e3953354a6a8344e616ed314d7251\",\"0x0\",\"latest\"]," + "\"id\":1}"); } @@ -122,7 +122,7 @@ public void testEthGetTransactionCount() throws Exception { web3j.platonGetTransactionCount("0x407d73d8a49eeb85d32cf465507dd71d507100c1", DefaultBlockParameterName.LATEST).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getTransactionCount\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getTransactionCount\"," + "\"params\":[\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\"latest\"]," + "\"id\":1}"); } @@ -133,7 +133,7 @@ public void testEthGetBlockTransactionCountByHash() throws Exception { "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").send(); //CHECKSTYLE:OFF - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getBlockTransactionCountByHash\",\"params\":[\"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getBlockTransactionCountByHash\",\"params\":[\"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -142,7 +142,7 @@ public void testEthGetBlockTransactionCountByNumber() throws Exception { web3j.platonGetBlockTransactionCountByNumber( DefaultBlockParameter.valueOf(Numeric.toBigInt("0xe8"))).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getBlockTransactionCountByNumber\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getBlockTransactionCountByNumber\"," + "\"params\":[\"0xe8\"],\"id\":1}"); } @@ -151,7 +151,7 @@ public void testEthGetCode() throws Exception { web3j.platonGetCode("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", DefaultBlockParameter.valueOf(Numeric.toBigInt("0x2"))).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getCode\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getCode\"," + "\"params\":[\"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\",\"0x2\"],\"id\":1}"); } @@ -160,7 +160,7 @@ public void testEthSign() throws Exception { web3j.platonSign("0x8a3106a3e50576d4b6794a0e74d3bb5f8c9acaab", "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_sign\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_sign\"," + "\"params\":[\"0x8a3106a3e50576d4b6794a0e74d3bb5f8c9acaab\"," + "\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"]," + "\"id\":1}"); @@ -179,7 +179,7 @@ public void testEthSendTransaction() throws Exception { + "970870f072445675058bb8eb970870f072445675")).send(); //CHECKSTYLE:OFF - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_sendTransaction\",\"params\":[{\"from\":\"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\"to\":\"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\"gas\":\"0x76c0\",\"gasPrice\":\"0x9184e72a000\",\"value\":\"0x9184e72a\",\"data\":\"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\",\"nonce\":\"0x1\"}],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_sendTransaction\",\"params\":[{\"from\":\"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\"to\":\"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\"gas\":\"0x76c0\",\"gasPrice\":\"0x9184e72a000\",\"value\":\"0x9184e72a\",\"data\":\"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\",\"nonce\":\"0x1\"}],\"id\":1}"); //CHECKSTYLE:ON } @@ -190,7 +190,7 @@ public void testEthSendRawTransaction() throws Exception { + "072445675058bb8eb970870f072445675").send(); //CHECKSTYLE:OFF - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_sendRawTransaction\",\"params\":[\"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_sendRawTransaction\",\"params\":[\"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -203,7 +203,7 @@ public void testEthCall() throws Exception { "0x0"), DefaultBlockParameter.valueOf("latest")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_call\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_call\"," + "\"params\":[{\"from\":\"0xa70e8dd61c5d32be8058bb8eb970870f07233155\"," + "\"to\":\"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\"data\":\"0x0\"}," + "\"latest\"],\"id\":1}"); @@ -216,7 +216,7 @@ public void testEthEstimateGas() throws Exception { "0xa70e8dd61c5d32be8058bb8eb970870f07233155", "0x52b93c80364dc2dd4444c146d73b9836bbbb2b3f", "0x0")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_estimateGas\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_estimateGas\"," + "\"params\":[{\"from\":\"0xa70e8dd61c5d32be8058bb8eb970870f07233155\"," + "\"to\":\"0x52b93c80364dc2dd4444c146d73b9836bbbb2b3f\",\"data\":\"0x0\"}]," + "\"id\":1}"); @@ -229,7 +229,7 @@ public void testEthEstimateGasContractCreation() throws Exception { "0x52b93c80364dc2dd4444c146d73b9836bbbb2b3f", BigInteger.ONE, BigInteger.TEN, "")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_estimateGas\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_estimateGas\"," + "\"params\":[{\"from\":\"0x52b93c80364dc2dd4444c146d73b9836bbbb2b3f\"," + "\"gasPrice\":\"0xa\",\"data\":\"0x\",\"nonce\":\"0x1\"}],\"id\":1}"); } @@ -240,7 +240,7 @@ public void testEthGetBlockByHash() throws Exception { "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true).send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"platon_getBlockByHash\",\"params\":[" + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getBlockByHash\",\"params\":[" + "\"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331\"" + ",true],\"id\":1}"); } @@ -250,7 +250,7 @@ public void testEthGetBlockByNumber() throws Exception { web3j.platonGetBlockByNumber( DefaultBlockParameter.valueOf(Numeric.toBigInt("0x1b4")), true).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getBlockByNumber\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getBlockByNumber\"," + "\"params\":[\"0x1b4\",true],\"id\":1}"); } @@ -259,7 +259,7 @@ public void testEthGetTransactionByHash() throws Exception { web3j.platonGetTransactionByHash( "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getTransactionByHash\",\"params\":[" + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getTransactionByHash\",\"params\":[" + "\"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238\"]," + "\"id\":1}"); } @@ -271,7 +271,7 @@ public void testEthGetTransactionByBlockHashAndIndex() throws Exception { BigInteger.ZERO).send(); //CHECKSTYLE:OFF - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getTransactionByBlockHashAndIndex\",\"params\":[\"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331\",\"0x0\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getTransactionByBlockHashAndIndex\",\"params\":[\"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331\",\"0x0\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -280,7 +280,7 @@ public void testEthGetTransactionByBlockNumberAndIndex() throws Exception { web3j.platonGetTransactionByBlockNumberAndIndex( DefaultBlockParameter.valueOf(Numeric.toBigInt("0x29c")), BigInteger.ZERO).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getTransactionByBlockNumberAndIndex\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getTransactionByBlockNumberAndIndex\"," + "\"params\":[\"0x29c\",\"0x0\"],\"id\":1}"); } @@ -289,7 +289,7 @@ public void testEthGetTransactionReceipt() throws Exception { web3j.platonGetTransactionReceipt( "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getTransactionReceipt\",\"params\":[" + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getTransactionReceipt\",\"params\":[" + "\"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238\"]," + "\"id\":1}"); } @@ -301,7 +301,7 @@ public void testEthNewFilter() throws Exception { web3j.platonNewFilter(ethFilter).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_newFilter\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_newFilter\"," + "\"params\":[{\"topics\":[\"0x12341234\"]}],\"id\":1}"); } @@ -309,7 +309,7 @@ public void testEthNewFilter() throws Exception { public void testEthNewBlockFilter() throws Exception { web3j.platonNewBlockFilter().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_newBlockFilter\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_newBlockFilter\"," + "\"params\":[],\"id\":1}"); } @@ -317,7 +317,7 @@ public void testEthNewBlockFilter() throws Exception { public void testEthNewPendingTransactionFilter() throws Exception { web3j.platonNewPendingTransactionFilter().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_newPendingTransactionFilter\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_newPendingTransactionFilter\"," + "\"params\":[],\"id\":1}"); } @@ -325,7 +325,7 @@ public void testEthNewPendingTransactionFilter() throws Exception { public void testEthUninstallFilter() throws Exception { web3j.platonUninstallFilter(Numeric.toBigInt("0xb")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_uninstallFilter\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_uninstallFilter\"," + "\"params\":[\"0x0b\"],\"id\":1}"); } @@ -333,7 +333,7 @@ public void testEthUninstallFilter() throws Exception { public void testEthGetFilterChanges() throws Exception { web3j.platonGetFilterChanges(Numeric.toBigInt("0x16")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getFilterChanges\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getFilterChanges\"," + "\"params\":[\"0x16\"],\"id\":1}"); } @@ -341,7 +341,7 @@ public void testEthGetFilterChanges() throws Exception { public void testEthGetFilterLogs() throws Exception { web3j.platonGetFilterLogs(Numeric.toBigInt("0x16")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getFilterLogs\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getFilterLogs\"," + "\"params\":[\"0x16\"],\"id\":1}"); } @@ -351,7 +351,7 @@ public void testEthGetLogs() throws Exception { "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")) .send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"platon_getLogs\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getLogs\"," + "\"params\":[{\"topics\":[" + "\"0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b\"]}]," + "\"id\":1}"); @@ -365,7 +365,7 @@ public void testEthGetLogsWithNumericBlockRange() throws Exception { .send(); verifyResult( - "{\"jsonrpc\":\"2.0\",\"method\":\"platon_getLogs\"," + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"platon_getLogs\"," + "\"params\":[{\"topics\":[],\"fromBlock\":\"0xe8\"," + "\"toBlock\":\"latest\",\"address\":[\"\"]}],\"id\":1}"); } @@ -374,7 +374,7 @@ public void testEthGetLogsWithNumericBlockRange() throws Exception { public void testDbPutString() throws Exception { web3j.dbPutString("testDB", "myKey", "myString").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"db_putString\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"db_putString\"," + "\"params\":[\"testDB\",\"myKey\",\"myString\"],\"id\":1}"); } @@ -382,7 +382,7 @@ public void testDbPutString() throws Exception { public void testDbGetString() throws Exception { web3j.dbGetString("testDB", "myKey").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"db_getString\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"db_getString\"," + "\"params\":[\"testDB\",\"myKey\"],\"id\":1}"); } @@ -390,7 +390,7 @@ public void testDbGetString() throws Exception { public void testDbPutHex() throws Exception { web3j.dbPutHex("testDB", "myKey", "0x68656c6c6f20776f726c64").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"db_putHex\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"db_putHex\"," + "\"params\":[\"testDB\",\"myKey\",\"0x68656c6c6f20776f726c64\"],\"id\":1}"); } @@ -398,7 +398,7 @@ public void testDbPutHex() throws Exception { public void testDbGetHex() throws Exception { web3j.dbGetHex("testDB", "myKey").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"db_getHex\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"db_getHex\"," + "\"params\":[\"testDB\",\"myKey\"],\"id\":1}"); } @@ -406,7 +406,7 @@ public void testDbGetHex() throws Exception { public void testShhVersion() throws Exception { web3j.shhVersion().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_version\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_version\"," + "\"params\":[],\"id\":1}"); } @@ -421,7 +421,7 @@ public void testShhPost() throws Exception { Numeric.toBigInt("0x64"), Numeric.toBigInt("0x64"))).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_post\",\"params\":[{\"from\":\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\",\"to\":\"0x3e245533f97284d442460f2998cd41858798ddf04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a0d4d661997d3940272b717b1\",\"topics\":[\"0x776869737065722d636861742d636c69656e74\",\"0x4d5a695276454c39425154466b61693532\"],\"payload\":\"0x7b2274797065223a226d6\",\"priority\":\"0x64\",\"ttl\":\"0x64\"}],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_post\",\"params\":[{\"from\":\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\",\"to\":\"0x3e245533f97284d442460f2998cd41858798ddf04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a0d4d661997d3940272b717b1\",\"topics\":[\"0x776869737065722d636861742d636c69656e74\",\"0x4d5a695276454c39425154466b61693532\"],\"payload\":\"0x7b2274797065223a226d6\",\"priority\":\"0x64\",\"ttl\":\"0x64\"}],\"id\":1}"); //CHECKSTYLE:ON } @@ -429,7 +429,7 @@ public void testShhPost() throws Exception { public void testShhNewIdentity() throws Exception { web3j.shhNewIdentity().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_newIdentity\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_newIdentity\",\"params\":[],\"id\":1}"); } @Test @@ -437,7 +437,7 @@ public void testShhHasIdentity() throws Exception { //CHECKSTYLE:OFF web3j.shhHasIdentity("0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_hasIdentity\",\"params\":[\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_hasIdentity\",\"params\":[\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -445,7 +445,7 @@ public void testShhHasIdentity() throws Exception { public void testShhNewGroup() throws Exception { web3j.shhNewGroup().send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_newGroup\",\"params\":[],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_newGroup\",\"params\":[],\"id\":1}"); } @Test @@ -453,7 +453,7 @@ public void testShhAddToGroup() throws Exception { //CHECKSTYLE:OFF web3j.shhAddToGroup("0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1").send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_addToGroup\",\"params\":[\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_addToGroup\",\"params\":[\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"],\"id\":1}"); //CHECKSTYLE:ON } @@ -464,7 +464,7 @@ public void testShhNewFilter() throws Exception { new ShhFilter("0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1") .addSingleTopic("0x12341234bf4b564f")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_newFilter\",\"params\":[{\"topics\":[\"0x12341234bf4b564f\"],\"to\":\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"}],\"id\":1}"); + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_newFilter\",\"params\":[{\"topics\":[\"0x12341234bf4b564f\"],\"to\":\"0x04f96a5e25610293e42a73908e93ccc8c4d4dc0edcfa9fa872f50cb214e08ebf61a03e245533f97284d442460f2998cd41858798ddfd4d661997d3940272b717b1\"}],\"id\":1}"); //CHECKSTYLE:ON } @@ -472,7 +472,7 @@ public void testShhNewFilter() throws Exception { public void testShhUninstallFilter() throws Exception { web3j.shhUninstallFilter(Numeric.toBigInt("0x7")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_uninstallFilter\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_uninstallFilter\"," + "\"params\":[\"0x07\"],\"id\":1}"); } @@ -480,7 +480,7 @@ public void testShhUninstallFilter() throws Exception { public void testShhGetFilterChanges() throws Exception { web3j.shhGetFilterChanges(Numeric.toBigInt("0x7")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_getFilterChanges\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_getFilterChanges\"," + "\"params\":[\"0x07\"],\"id\":1}"); } @@ -488,7 +488,38 @@ public void testShhGetFilterChanges() throws Exception { public void testShhGetMessages() throws Exception { web3j.shhGetMessages(Numeric.toBigInt("0x7")).send(); - verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"shh_getMessages\"," + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"shh_getMessages\"," + "\"params\":[\"0x07\"],\"id\":1}"); } + + @Test + public void testAdminAddPeer() throws Exception { + web3j.adminAddPeer("url").send(); + + verifyResult( + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"admin_addPeer\",\"params\":[\"url\"],\"id\":1}"); + } + + @Test + public void testAdminDataDir() throws Exception { + web3j.adminDataDir().send(); + + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"admin_datadir\",\"params\":[],\"id\":1}"); + } + + @Test + public void testAdminRemovePeer() throws Exception { + web3j.adminRemovePeer("url").send(); + + verifyResult( + "{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"admin_removePeer\",\"params\":[\"url\"],\"id\":1}"); + } + + @Test + public void testTxPoolStatus() throws Exception { + web3j.txPoolStatus().send(); + + verifyResult("{\"jsonrpc\":\"2.0\",\"bech32\":true,\"method\":\"txpool_status\",\"params\":[],\"id\":1}"); + } + } diff --git a/core/src/test/java/com/platon/protocol/core/ResponseTest.java b/core/src/test/java/com/platon/protocol/core/ResponseTest.java index f5a8f753..88f138a6 100644 --- a/core/src/test/java/com/platon/protocol/core/ResponseTest.java +++ b/core/src/test/java/com/platon/protocol/core/ResponseTest.java @@ -1,6 +1,8 @@ package com.platon.protocol.core; import com.platon.protocol.ResponseTester; +import com.platon.protocol.admin.methods.response.TxPoolStatus; +import com.platon.protocol.admin.methods.response.admin.AdminDataDir; import com.platon.protocol.core.methods.response.*; import com.platon.utils.Numeric; import org.junit.Test; @@ -1257,4 +1259,32 @@ public void testSshMessages() { ShhMessages shhMessages = deserialiseResponse(ShhMessages.class); assertThat(shhMessages.getMessages(), equalTo(messages)); } + + @Test + public void testAdminDataDir() { + buildResponse( + "{\n" + + " \"jsonrpc\":\"2.0\",\n" + + " \"id\":22,\n" + + " \"result\":\"sampleDir\"\n" + + "}"); + + AdminDataDir dataDir = deserialiseResponse(AdminDataDir.class); + assertEquals(dataDir.getDataDir(), "sampleDir"); + } + + @Test + public void testTxPoolStatus() { + buildResponse( + "{\n" + + " \"jsonrpc\":\"2.0\",\n" + + " \"id\":22,\n" + + " \"result\":{ \"pending\": \"0x9\",\n" + + " \"queued\": \"0x7\"}\n" + + "}"); + + TxPoolStatus status = deserialiseResponse(TxPoolStatus.class); + assert(status.getPending() == 9); + assert(status.getQueued() == 7); + } } diff --git a/crypto/src/main/java/com/platon/crypto/Base58.java b/crypto/src/main/java/com/platon/crypto/Base58.java new file mode 100644 index 00000000..30b56eb7 --- /dev/null +++ b/crypto/src/main/java/com/platon/crypto/Base58.java @@ -0,0 +1,75 @@ +/* + * Copyright 2011 Google Inc. + * Copyright 2018 Andreas Schildbach + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.platon.crypto; + + +import java.util.Arrays; + +public class Base58 { + private static final char[] ALPHABET = + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + private static final char ENCODED_ZERO = ALPHABET[0]; + + /** + * Encodes the given bytes as a base58 string (no checksum is appended). + * + * @param input the bytes to encode + * @return the base58-encoded string + */ + public static String encode(byte[] input) { + if (input.length == 0) { + return ""; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input.length && input[zeros] == 0) { + ++zeros; + } + // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) + input = Arrays.copyOf(input, input.length); // since we modify it in-place + char[] encoded = new char[input.length * 2]; // upper bound + int outputStart = encoded.length; + for (int inputStart = zeros; inputStart < input.length; ) { + encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; + if (input[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Preserve exactly as many leading encoded zeros in output as there + // were leading zeros in input. + while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { + ++outputStart; + } + while (--zeros >= 0) { + encoded[--outputStart] = ENCODED_ZERO; + } + // Return encoded string (including encoded leading zeros). + return new String(encoded, outputStart, encoded.length - outputStart); + } + + private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { + // this is just long division which accounts for the base of the input digits + int remainder = 0; + for (int i = firstDigit; i < number.length; i++) { + int digit = (int) number[i] & 0xFF; + int temp = remainder * base + digit; + number[i] = (byte) (temp / divisor); + remainder = temp % divisor; + } + return (byte) remainder; + } +} \ No newline at end of file diff --git a/crypto/src/main/java/com/platon/crypto/Bip32ECKeyPair.java b/crypto/src/main/java/com/platon/crypto/Bip32ECKeyPair.java new file mode 100644 index 00000000..41a75548 --- /dev/null +++ b/crypto/src/main/java/com/platon/crypto/Bip32ECKeyPair.java @@ -0,0 +1,172 @@ +package com.platon.crypto; + +import com.platon.utils.Numeric; +import org.bouncycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import static com.platon.crypto.Hash.hmacSha512; +import static com.platon.crypto.Hash.sha256hash160; + +/** + * BIP-32 key pair. + * + *

Adapted from: + * https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java + */ +public class Bip32ECKeyPair extends ECKeyPair { + static final int HARDENED_BIT = 0x80000000; + + private final boolean parentHasPrivate; + private final int childNumber; + private final int depth; + private final byte[] chainCode; + private int parentFingerprint; + + private ECPoint publicKeyPoint; + + public Bip32ECKeyPair(BigInteger privateKey, BigInteger publicKey, int childNumber, + byte[] chainCode, Bip32ECKeyPair parent) { + super(privateKey, publicKey); + this.parentHasPrivate = parent != null && parent.hasPrivateKey(); + this.childNumber = childNumber; + this.depth = parent == null ? 0 : parent.depth + 1; + this.chainCode = Arrays.copyOf(chainCode, chainCode.length); + this.parentFingerprint = parent != null ? parent.getFingerprint() : 0; + } + + public static Bip32ECKeyPair create(BigInteger privateKey, byte[] chainCode) { + return new Bip32ECKeyPair(privateKey, Sign.publicKeyFromPrivate(privateKey), + 0, chainCode, null); + } + + public static Bip32ECKeyPair create(byte[] privateKey, byte[] chainCode) { + return create(Numeric.toBigInt(privateKey), chainCode); + } + + public static Bip32ECKeyPair generateKeyPair(byte[] seed) { + byte[] i = hmacSha512("Bitcoin seed".getBytes(), seed); + byte[] il = Arrays.copyOfRange(i, 0, 32); + byte[] ir = Arrays.copyOfRange(i, 32, 64); + Arrays.fill(i, (byte) 0); + Bip32ECKeyPair keypair = Bip32ECKeyPair.create(il, ir); + Arrays.fill(il, (byte) 0); + Arrays.fill(ir, (byte) 0); + + return keypair; + } + + public static Bip32ECKeyPair deriveKeyPair(Bip32ECKeyPair master, int[] path) { + Bip32ECKeyPair curr = master; + if (path != null) { + for (int childNumber : path) { + curr = curr.deriveChildKey(childNumber); + } + } + + return curr; + } + + public Bip32ECKeyPair deriveChildKey(int childNumber) { + if (!hasPrivateKey()) { + byte[] parentPublicKey = getPublicKeyPoint().getEncoded(true); + ByteBuffer data = ByteBuffer.allocate(37); + data.put(parentPublicKey); + data.putInt(childNumber); + byte[] i = hmacSha512(getChainCode(), data.array()); + byte[] il = Arrays.copyOfRange(i, 0, 32); + byte[] chainCode = Arrays.copyOfRange(i, 32, 64); + Arrays.fill(i, (byte) 0); + BigInteger ilInt = new BigInteger(1, il); + Arrays.fill(il, (byte) 0); + ECPoint ki = Sign.publicPointFromPrivate(ilInt).add(getPublicKeyPoint()); + + return new Bip32ECKeyPair(null, + Sign.publicFromPoint(ki.getEncoded(true)), + childNumber, chainCode, this); + } else { + ByteBuffer data = ByteBuffer.allocate(37); + if (isHardened(childNumber)) { + data.put(getPrivateKeyBytes33()); + } else { + byte[] parentPublicKey = getPublicKeyPoint().getEncoded(true); + data.put(parentPublicKey); + } + data.putInt(childNumber); + byte[] i = hmacSha512(getChainCode(), data.array()); + byte[] il = Arrays.copyOfRange(i, 0, 32); + byte[] chainCode = Arrays.copyOfRange(i, 32, 64); + Arrays.fill(i, (byte) 0); + BigInteger ilInt = new BigInteger(1, il); + Arrays.fill(il, (byte) 0); + BigInteger privateKey = getPrivateKey().add(ilInt).mod(Sign.CURVE.getN()); + + return new Bip32ECKeyPair(privateKey, Sign.publicKeyFromPrivate(privateKey), + childNumber, chainCode, this); + } + } + + private int getFingerprint() { + byte[] id = getIdentifier(); + return id[3] & 0xFF | (id[2] & 0xFF) << 8 | (id[1] & 0xFF) << 16 | (id[0] & 0xFF) << 24; + } + + public int getDepth() { + return depth; + } + + public int getParentFingerprint() { + return parentFingerprint; + } + + public byte[] getChainCode() { + return chainCode; + } + + public int getChildNumber() { + return childNumber; + } + + private byte[] getIdentifier() { + return sha256hash160(getPublicKeyPoint().getEncoded(true)); + } + + public ECPoint getPublicKeyPoint() { + if (publicKeyPoint == null) { + publicKeyPoint = Sign.publicPointFromPrivate(getPrivateKey()); + } + return publicKeyPoint; + } + + public byte[] getPrivateKeyBytes33() { + final int numBytes = 33; + + byte[] bytes33 = new byte[numBytes]; + byte[] priv = bigIntegerToBytes32(getPrivateKey()); + System.arraycopy(priv, 0, bytes33, numBytes - priv.length, priv.length); + return bytes33; + } + + private boolean hasPrivateKey() { + return this.getPrivateKey() != null || parentHasPrivate; + } + + private static byte[] bigIntegerToBytes32(BigInteger b) { + final int numBytes = 32; + + byte[] src = b.toByteArray(); + byte[] dest = new byte[numBytes]; + boolean isFirstByteOnlyForSign = src[0] == 0; + int length = isFirstByteOnlyForSign ? src.length - 1 : src.length; + int srcPos = isFirstByteOnlyForSign ? 1 : 0; + int destPos = numBytes - length; + System.arraycopy(src, srcPos, dest, destPos, length); + return dest; + } + + private static boolean isHardened(int a) { + return (a & HARDENED_BIT) != 0; + } +} diff --git a/crypto/src/main/java/com/platon/crypto/Bip39Wallet.java b/crypto/src/main/java/com/platon/crypto/Bip39Wallet.java index b4edb416..51fa9cd8 100644 --- a/crypto/src/main/java/com/platon/crypto/Bip39Wallet.java +++ b/crypto/src/main/java/com/platon/crypto/Bip39Wallet.java @@ -28,11 +28,4 @@ public String getMnemonic() { return mnemonic; } - @Override - public String toString() { - return "Bip39Wallet{" - + "filename='" + filename + '\'' - + ", mnemonic='" + mnemonic + '\'' - + '}'; - } } diff --git a/crypto/src/main/java/com/platon/crypto/Bip44WalletUtils.java b/crypto/src/main/java/com/platon/crypto/Bip44WalletUtils.java new file mode 100644 index 00000000..ce81769a --- /dev/null +++ b/crypto/src/main/java/com/platon/crypto/Bip44WalletUtils.java @@ -0,0 +1,78 @@ +package com.platon.crypto; + +import java.io.File; +import java.io.IOException; + +import static com.platon.crypto.Bip32ECKeyPair.HARDENED_BIT; + +public class Bip44WalletUtils extends WalletUtils { + + /** + * Generates a BIP-44 compatible Ethereum wallet on top of BIP-39 generated seed. + * + * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed + * @param destinationDirectory The directory containing the wallet + * @return A BIP-39 compatible Ethereum wallet + * @throws CipherException if the underlying cipher is not available + * @throws IOException if the destination cannot be written to + */ + public static Bip39Wallet generateBip44Wallet(String password, File destinationDirectory) + throws CipherException, IOException { + return generateBip44Wallet(password, destinationDirectory, false); + } + + /** + * Generates a BIP-44 compatible Ethereum wallet on top of BIP-39 generated seed. + * + * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed + * @param destinationDirectory The directory containing the wallet + * @param testNet should use the testNet derive path + * @return A BIP-39 compatible Ethereum wallet + * @throws CipherException if the underlying cipher is not available + * @throws IOException if the destination cannot be written to + */ + public static Bip39Wallet generateBip44Wallet(String password, File destinationDirectory, + boolean testNet) + throws CipherException, IOException { + byte[] initialEntropy = new byte[16]; + SecureRandomUtils.secureRandom().nextBytes(initialEntropy); + + String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy); + byte[] seed = MnemonicUtils.generateSeed(mnemonic, null); + + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair, testNet); + + String walletFile = generateWalletFile(password, bip44Keypair, destinationDirectory, false); + + return new Bip39Wallet(walletFile, mnemonic); + } + + public static Bip32ECKeyPair generateBip44KeyPair(Bip32ECKeyPair master) { + return generateBip44KeyPair(master, false); + } + + public static Bip32ECKeyPair generateBip44KeyPair(Bip32ECKeyPair master, boolean testNet) { + if (testNet) { + // /m/44'/0'/0 + final int[] path = {44 | HARDENED_BIT, 0 | HARDENED_BIT, 0 | HARDENED_BIT, 0}; + return Bip32ECKeyPair.deriveKeyPair(master, path); + } else { + // m/44'/206'/0'/0 + final int[] path = {44 | HARDENED_BIT, 206 | HARDENED_BIT, 0 | HARDENED_BIT, 0}; + return Bip32ECKeyPair.deriveKeyPair(master, path); + } + } + + public static Credentials loadBip44Credentials(String password, String mnemonic) { + return loadBip44Credentials(password, mnemonic, false); + } + + public static Credentials loadBip44Credentials(String password, String mnemonic, + boolean testNet) { + byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair, testNet); + return Credentials.create(bip44Keypair); + } +} \ No newline at end of file diff --git a/crypto/src/main/java/com/platon/crypto/Sign.java b/crypto/src/main/java/com/platon/crypto/Sign.java index a23ccd75..bef97729 100644 --- a/crypto/src/main/java/com/platon/crypto/Sign.java +++ b/crypto/src/main/java/com/platon/crypto/Sign.java @@ -211,7 +211,7 @@ public static BigInteger publicKeyFromPrivate(BigInteger privKey) { /** * Returns public key point from the given private key. */ - private static ECPoint publicPointFromPrivate(BigInteger privKey) { + public static ECPoint publicPointFromPrivate(BigInteger privKey) { /* * TODO: FixedPointCombMultiplier currently doesn't support scalars longer than the group * order, but that could change in future versions. @@ -222,6 +222,16 @@ private static ECPoint publicPointFromPrivate(BigInteger privKey) { return new FixedPointCombMultiplier().multiply(CURVE.getG(), privKey); } + /** + * Returns public key point from the given curve. + * + * @param bits representing the point on the curve + * @return BigInteger encoded public key + */ + public static BigInteger publicFromPoint(byte[] bits) { + return new BigInteger(1, Arrays.copyOfRange(bits, 1, bits.length)); // remove prefix + } + public static class SignatureData { private final byte[] v; private final byte[] r; diff --git a/crypto/src/main/java/com/platon/crypto/WalletUtils.java b/crypto/src/main/java/com/platon/crypto/WalletUtils.java index 1f1e97ea..6336b54e 100644 --- a/crypto/src/main/java/com/platon/crypto/WalletUtils.java +++ b/crypto/src/main/java/com/platon/crypto/WalletUtils.java @@ -17,6 +17,8 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.platon.crypto.Hash.sha256; import static com.platon.crypto.Keys.ADDRESS_LENGTH_IN_HEX; @@ -248,7 +250,7 @@ public static String getTestnetKeyDirectory() { public static String getMainnetKeyDirectory() { return String.format("%s%skeystore", getDefaultKeyDirectory(), File.separator); } - + /** * Get keystore destination directory for a Rinkeby network. * @return a String containing destination directory @@ -264,6 +266,12 @@ public static boolean isValidPrivateKey(String privateKey) { } public static boolean isValidAddress(String input) { + //exclude blank characters and uppercase letters + Pattern pattern = Pattern.compile("^[a-z0-9]+$"); + Matcher matcher = pattern.matcher(input); + if(!matcher.find()){ + return false; + } String cleanInput; try{ byte [] bytes = Bech32.addressDecode(input); diff --git a/crypto/src/test/java/com/platon/crypto/Bip32Test.java b/crypto/src/test/java/com/platon/crypto/Bip32Test.java new file mode 100644 index 00000000..aa0ccd8d --- /dev/null +++ b/crypto/src/test/java/com/platon/crypto/Bip32Test.java @@ -0,0 +1,158 @@ +package com.platon.crypto; + +import com.platon.utils.Numeric; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.nio.ByteBuffer; + +import static com.platon.crypto.Bip32ECKeyPair.HARDENED_BIT; +import static com.platon.crypto.Hash.sha256; + +/** + * BIP-32 implementation test. + * + *

Test vectors taken from BIP-32 definition + * https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki + */ +public class Bip32Test { + @SuppressWarnings("checkstyle:LineLength") + @Test + public void deriveKeyPairVector1() { + // Chain m + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", + null); + + // Chain m/0H + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", + "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", + new int[]{0 | HARDENED_BIT}); + + // Chain m/0H/1 + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", + "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", + new int[]{0 | HARDENED_BIT, 1}); + + // Chain m/0H/1/2H + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", + "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", + new int[]{0 | HARDENED_BIT, 1, 2 | HARDENED_BIT}); + + // Chain m/0H/1/2H/2 + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", + "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", + new int[]{0 | HARDENED_BIT, 1, 2 | HARDENED_BIT, 2}); + + // Chain m/0H/1/2H/2/1000000000 + testGenerated("000102030405060708090a0b0c0d0e0f", + "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", + "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", + new int[]{0 | HARDENED_BIT, 1, 2 | HARDENED_BIT, 2, 1000000000}); + } + + @SuppressWarnings("checkstyle:LineLength") + @Test + public void deriveKeyPairVector2() { + // Chain m + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", + "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + null); + + // Chain m/0 + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", + "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + new int[]{0}); + + // Chain m/0/2147483647H + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", + "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", + new int[]{0, 2147483647 | HARDENED_BIT}); + + // Chain m/0/2147483647H/1 + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", + "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", + new int[]{0, 2147483647 | HARDENED_BIT, 1}); + + // Chain m/0/2147483647H/1/2147483646H + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", + "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", + new int[]{0, 2147483647 | HARDENED_BIT, 1, 2147483646 | HARDENED_BIT}); + + // Chain m/0/2147483647H/1/2147483646H/2 + testGenerated("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", + new int[]{0, 2147483647 | HARDENED_BIT, 1, 2147483646 | HARDENED_BIT, 2}); + } + + @SuppressWarnings("checkstyle:LineLength") + @Test + public void deriveKeyPairVector3() { + // Chain m + testGenerated("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be", + "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6", + "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", + null); + + // Chain m/0H + testGenerated("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be", + "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", + "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", + new int[]{0 | HARDENED_BIT}); + } + + private void testGenerated(String seed, String expectedPriv, String expectedPub, int[] path) { + Bip32ECKeyPair pair = Bip32ECKeyPair.generateKeyPair(Numeric.hexStringToByteArray(seed)); + assertNotNull(pair); + + pair = Bip32ECKeyPair.deriveKeyPair(pair, path); + assertNotNull(pair); + + assertEquals(expectedPriv, Base58.encode(addChecksum(serializePrivate(pair)))); + assertEquals(expectedPub, Base58.encode(addChecksum(serializePublic(pair)))); + } + + static byte[] addChecksum(byte[] input) { + int inputLength = input.length; + byte[] checksummed = new byte[inputLength + 4]; + System.arraycopy(input, 0, checksummed, 0, inputLength); + byte[] checksum = hashTwice(input); + System.arraycopy(checksum, 0, checksummed, inputLength, 4); + return checksummed; + } + + static byte[] serializePublic(Bip32ECKeyPair pair) { + return serialize(pair, 0x0488B21E, true); + } + + static byte[] serializePrivate(Bip32ECKeyPair pair) { + return serialize(pair, 0x0488ADE4, false); + } + + private static byte[] hashTwice(byte[] input) { + return sha256(sha256(input)); + } + + private static byte[] serialize(Bip32ECKeyPair pair, int header, boolean pub) { + ByteBuffer ser = ByteBuffer.allocate(78); + ser.putInt(header); + ser.put((byte) pair.getDepth()); + ser.putInt(pair.getParentFingerprint()); + ser.putInt(pair.getChildNumber()); + ser.put(pair.getChainCode()); + ser.put(pub ? pair.getPublicKeyPoint().getEncoded(true) : pair.getPrivateKeyBytes33()); + return ser.array(); + } +} \ No newline at end of file diff --git a/crypto/src/test/java/com/platon/crypto/Bip44WalletUtilsTest.java b/crypto/src/test/java/com/platon/crypto/Bip44WalletUtilsTest.java new file mode 100644 index 00000000..8f495747 --- /dev/null +++ b/crypto/src/test/java/com/platon/crypto/Bip44WalletUtilsTest.java @@ -0,0 +1,112 @@ +package com.platon.crypto; + +import com.platon.parameters.NetworkParameters; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static com.platon.crypto.Bip32Test.*; +import static com.platon.crypto.SampleKeys.PASSWORD; +import static com.platon.crypto.WalletUtilsTest.createTempDir; +import static org.junit.Assert.assertEquals; + +import java.io.File; + +public class Bip44WalletUtilsTest { + + private File tempDir; + + @Before + public void setUp() throws Exception { + tempDir = createTempDir(); + } + + @After + public void tearDown() throws Exception { + for (File file:tempDir.listFiles()) { + file.delete(); + } + tempDir.delete(); + } + + @SuppressWarnings("checkstyle:LineLength") + @Test + public void generateBip44KeyPair() { + String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above"; + byte[] seed = MnemonicUtils.generateSeed(mnemonic, null); + String seedStr = bytesToHex(seed); + assertEquals("f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5", + seedStr); + + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + assertEquals("xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD", + Base58.encode(addChecksum(serializePrivate(masterKeypair)))); + + Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair); + + assertEquals("xprvA1UQTpt1bAYoZCH2VvKERY9jEAoGQZiPdrZWg5Z4YoCGcBeSgri9JwdVsUM5HGbhZ9UmcGeSW2MNZCtJwXofUHB5KcswT1Sgr3sDfQhhZqK", + Base58.encode(addChecksum(serializePrivate(bip44Keypair)))); + assertEquals("xpub6ETksLQuRY76mgMVbwrEng6TnCdkp2SF15V7UTxg78jFUyybEQ2Prjwyimr7fPdjGz6UugnjXL4Gt8gNAocgFTtKjRp6azm9NCh7DDpE5oR", + Base58.encode(addChecksum(serializePublic(bip44Keypair)))); + } + + @SuppressWarnings("checkstyle:LineLength") + @Test + public void deriveChildKey() { + NetworkParameters.selectAlaya(); + String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above"; + byte[] seed = MnemonicUtils.generateSeed(mnemonic, null); + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair); + for (int i = 0; i < 1; i++) { + Bip32ECKeyPair bip32ECKeyPair = bip44Keypair.deriveChildKey(i); + String address = Credentials.create(bip32ECKeyPair).getAddress(); + Assert.assertEquals("atp17rp5teqsstfnn8w80vvmthepn78p0dnn07f9rz",address); + } + } + + @SuppressWarnings("checkstyle:LineLength") + @Test + public void generateBip44KeyPairTestNet() { + String mnemonic = "spider elbow fossil truck deal circle divert sleep safe report laundry above"; + byte[] seed = MnemonicUtils.generateSeed(mnemonic, null); + String seedStr = bytesToHex(seed); + assertEquals("f0d2ab78b96acd147119abad1cd70eb4fec4f0e0a95744cf532e6a09347b08101213b4cbf50eada0eb89cba444525fe28e69707e52aa301c6b47ce1c5ef82eb5", + seedStr); + + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + assertEquals("xprv9s21ZrQH143K2yA9Cdad5gjqHRC7apVUgEyYq5jXeXigDZ3PfEnps44tJprtMXr7PZivEsin6Qrbad7PuiEy4tn5jAEK6A3U46f9KvfRCmD", + Base58.encode(addChecksum(serializePrivate(masterKeypair)))); + + Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair, true); + + assertEquals("xprv9zhLxq63By3SX5hAMKnxjGy7L18bnn7GzDQv53eYYqeRX9M82riC1dqovamttwFpk2ZkDQxgcikBQzs1DTu2KShJJqnqgx83EftUB3k39uc", + Base58.encode(addChecksum(serializePrivate(bip44Keypair)))); + assertEquals("xpub6DghNLcw2LbjjZmdTMKy6Quqt2y6CEq8MSLWsS4A7BBQPwgGaQ2SZSAHmsrqBVxLegjW2mBfcvDBhpeEqCmucTTPJiNLHQkiDuKwHs9gEtk", + Base58.encode(addChecksum(serializePublic(bip44Keypair)))); + } + + @Test + public void testGenerateBip44Wallets() throws Exception { + Bip39Wallet wallet = Bip44WalletUtils.generateBip44Wallet(PASSWORD, tempDir); + byte[] seed = MnemonicUtils.generateSeed(wallet.getMnemonic(), PASSWORD); + Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); + Bip32ECKeyPair bip44Keypair = Bip44WalletUtils.generateBip44KeyPair(masterKeypair); + Credentials credentials = Credentials.create(bip44Keypair); + + assertEquals(credentials, + Bip44WalletUtils.loadBip44Credentials(PASSWORD, wallet.getMnemonic())); + } + + private String bytesToHex(byte[] bytes) { + final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + char[] chars = new char[2 * bytes.length]; + for (int i = 0; i < bytes.length; ++i) { + chars[2 * i] = HEX_CHARS[(bytes[i] & 0xF0) >>> 4]; + chars[2 * i + 1] = HEX_CHARS[bytes[i] & 0x0F]; + } + return new String(chars); + } +} \ No newline at end of file diff --git a/crypto/src/test/java/com/platon/crypto/SignTest.java b/crypto/src/test/java/com/platon/crypto/SignTest.java index 8e800ec7..60d6ef35 100644 --- a/crypto/src/test/java/com/platon/crypto/SignTest.java +++ b/crypto/src/test/java/com/platon/crypto/SignTest.java @@ -1,6 +1,7 @@ package com.platon.crypto; import com.platon.utils.Numeric; +import org.bouncycastle.math.ec.ECPoint; import org.junit.Ignore; import org.junit.Test; @@ -49,4 +50,11 @@ public void testInvalidSignature() throws SignatureException { Sign.signedMessageToKey( TEST_MESSAGE, new Sign.SignatureData((byte) 27, new byte[]{1}, new byte[]{0})); } + + @Test + public void testPublicKeyFromPrivatePoint() { + ECPoint point = Sign.publicPointFromPrivate(SampleKeys.PRIVATE_KEY); + assertThat(Sign.publicFromPoint(point.getEncoded(false)), + equalTo(SampleKeys.PUBLIC_KEY)); + } } diff --git a/crypto/src/test/java/com/platon/crypto/WalletUtilsTest.java b/crypto/src/test/java/com/platon/crypto/WalletUtilsTest.java index 27946df6..077aa0a1 100644 --- a/crypto/src/test/java/com/platon/crypto/WalletUtilsTest.java +++ b/crypto/src/test/java/com/platon/crypto/WalletUtilsTest.java @@ -207,7 +207,7 @@ public void testGetTestnetKeyDirectory() { } - private static File createTempDir() throws Exception { + static File createTempDir() throws Exception { return Files.createTempDirectory( WalletUtilsTest.class.getSimpleName() + "-testkeys").toFile(); } diff --git a/doc/Java-SDK-en.md b/doc/Java-SDK-en.md index 76932c4d..b035cfb8 100644 --- a/doc/Java-SDK-en.md +++ b/doc/Java-SDK-en.md @@ -16,7 +16,7 @@ Depending on the build tool, use the following methods to add related dependenci > Project configuration: ```xml - platon-public + alaya-public https://sdk.platon.network/nexus/content/groups/public/ ``` @@ -26,7 +26,7 @@ Depending on the build tool, use the following methods to add related dependenci com.alaya.sdk core - 0.16.0.0 + 0.16.1.0 ``` @@ -41,7 +41,7 @@ repositories { > gradle way of reference: ``` -compile "com.alaya.sdk:core:0.16.0.0" +compile "com.alaya.sdk:core:0.16.1.0" ``` ## Basic API Usage @@ -253,6 +253,78 @@ Request request = currentValidWeb3j.netPeerCount(); BigInteger req = request.send().getQuantity(); ``` +### adminAddPeer + +> Add a peer to the client node + +* **parameters** + + String : peer's URL + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse property is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminAddPeer("enode://0abaf3219f454f3d07b6cbcf3c10b6b4ccf605202868e2043b6f5db12b745df0604ef01ef4cb523adc6d9e14b83a76dd09f862e3fe77205d8ac83df707969b47@[::]:16789"); +Boolean resp = request.send().getResult(); +``` + +### adminRemovePeer + +> Remove a peer from the client node + +* **parameters** + + String : peer's URL + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse property is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminRemovePeer("enode://0abaf3219f454f3d07b6cbcf3c10b6b4ccf605202868e2043b6f5db12b745df0604ef01ef4cb523adc6d9e14b83a76dd09f862e3fe77205d8ac83df707969b47@[::]:16789"); +Boolean resp = request.send().getResult(); +``` + +### adminDataDir + +> Return the current node data directory + +* **parameters** + + no + +* **return value** + +```java +Request +``` + +The result in the AdminDataDir property is the corresponding storage data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminDataDir(); +String resp = request.send().getDataDir(); +``` + ### platonProtocolVersion > Returns the current platon protocol version @@ -1154,6 +1226,54 @@ Request request = currentValidWeb3j.dbGetHex(databaseName, keyName) String req = request.send(). GetStoredValue(); ``` +### txPoolStatus + +> Returns the status of the transaction pool + +* **parameters** + + no + +* **return value** + +```java +Request +``` + +The result in the TxPoolStatus property is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.txPoolStatus(); +TxPoolStatus txPoolStatus = request.send(); +``` + +### txPoolContent + +> Return transaction pool content + +* **parameters** + + no + +* **return value** + +```java +Request +``` + +The result in the TxPoolContent property is the corresponding storage data + +* **Example** + +```java +JsonRpc2_0Admin platonWeb3j = new JsonRpc2_0Admin(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.txPoolContent(); +TxPoolContent txPoolContent = request.send(); +``` + ### platonEvidences > Return double sign report data @@ -1377,21 +1497,21 @@ String debugEconomicConfig = req.send().getEconomicConfigStr(); ### getChainId -> Get chain ID +> Get chain ID -- **parameters** +* **parameters** - no + No -- **return value** +* **return value** ```java Request ``` -The String in the PlatonChainId property is the corresponding stored data +The String in the PlatonChainId attribute is the corresponding storage data -- **Example** +* **Example** ```java Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); @@ -1399,6 +1519,631 @@ Request req = platonWeb3j.getChainId(); BigInteger chainId = req.send().getChainId(); ``` +### adminNodeInfo + +> Retrieve all the information we know about the host node at protocol granularity + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the AdminNodeInfo attribute is the corresponding storage data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminNodeInfo nodeInfo = platonWeb3j.adminNodeInfo().send(); +``` + +### adminPeers + +> Retrieve all the information we know about each individual Peer at protocol granularity + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the AdminPeers attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminPeers adminPeers = platonWeb3j.adminPeers().send(); +``` + +### adminStartRPC + +> Start the HTTP RPC API server + +* **parameters** + + - String : host : Network address to listen on + - Integer : port : Network port to listen on + - String : cors : Cross-origin resource sharing header to be used + - String : apis : API modules to provide services through the service interface + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStartRPC("127.0.0.1", 6789, null, null).send(); +``` + +### adminStartWS + +> Start the websocket RPC API server + +* **parameters** + + - String : host : Network address to listen on + - Integer : port : Network port to listen on + - String : cors : Cross-origin resource sharing header to be used + - String : apis : API modules to provide services through the service interface + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = web3j.adminStartWS("127.0.0.1", 7789, null, null).send(); +``` + +### adminStopRPC + +> Close the currently started HTTP RPC end node + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStopRPC().send(); +``` + +### adminStopWS + +> Close the currently started WebSocket RPC end node + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStopWS().send(); +``` + +### adminExportChain + +> Export the current blockchain to a local file + +* **parameters** + + - String : file : file name + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.adminExportChain("1").send(); +``` + +### adminImportChain + +> Import blockchain from local file + +* **parameters** + + - String : file : file name + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.adminImportChain("1").send(); +``` + +### getWaitSlashingNodeList + +> Get the node with zero block, the list of nodes that are observed because of zero block + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The WaitSlashingNode list object in the DebugWaitSlashingNodeList property is the corresponding storage data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request req = platonWeb3j.getWaitSlashingNodeList(); +DebugWaitSlashingNodeList nodeList = req.send(); +``` + +### platonGetRawTransactionByHash + +> Returns the number of transaction bytes for a given Hash + +* **parameters** + + - String : hash : Transaction hash + +* **return value** + +```java +Request +``` + +The result in the PlatonRawTransaction attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByHash("0x5b99...").send(); +``` + +### platonGetRawTransactionByBlockHashAndIndex + +> Return transactions for a given block hash and index + +* **parameters** + + - String : hash : Block hash + - String : index : index + +* **return value** + +```java +Request +``` + +The result in the PlatonRawTransaction attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByBlockHashAndIndex("0xa34...", "0x1").send(); +``` + +### platonGetRawTransactionByBlockNumberAndIndex + +> Return transactions with a given block number and index + +* **parameters** + + - String : blockNumber : Block number + - String : index : index + +* **return value** + +```java +Request +``` + +The result in the PlatonRawTransaction attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByBlockNumberAndIndex("0x1", "0x1").send(); +``` + +### platonGetAddressHrp + +> Get chain Hrp + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the PlatonGetAddressHrp attribute is the corresponding storage data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonGetAddressHrp send = platonWeb3j.platonGetAddressHrp().send(); +``` + +### platonSignTransaction + +> The from account will be used to sign the given transaction. The node needs to have the account private key corresponding to the given sender address, and it needs to be unlocked + +* **parameters** + + - Transaction : transaction : Transaction object + +* **return value** + +```java +Request +``` + +The result in the PlatonSignTransaction attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PlatonSignTransaction send = platonWeb3j.platonSignTransaction(transaction).send(); +``` + +### minerSetGasPrice + +> Set the minimum gas price acceptable to miners + +* **parameters** + + - String : minGasPrice : Lowest gas price + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.minerSetGasPrice("0x1").send(); +``` + +### adminPeerEvents + +> Create an RPC subscription, which receives peer events from the node's p2p server + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the AdminPeerEvents attribute is the corresponding storage data + +* **Example** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminPeerEvents send = web3j.adminPeerEvents().send(); +``` + +### personalImportRawKey + +> Store the given hexadecimal coded key in the key directory and encrypt it with a password + +* **parameters** + + - String : keydata : Private key + - String : password : password + +* **return value** + +```java +Request +``` + +The result in the PersonalImportRawKey attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalImportRawKey key = admin.personalImportRawKey("03axxx", "000000").send(); +``` + +### personalLockAccount + +> Lock the account associated with a given address + +* **parameters** + + - String : address : Account address + +* **return value** + +```java +Request +``` + +The result in the BooleanResponse attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse response = admin.personalLockAccount("atp1cxxx").send(); +``` + +### personalSign + +> Use the given account to sign the transaction, the account needs to exist in the node's account library + +* **parameters** + + - String : message : The hexadecimal code string of the transaction + - String : accountId : Account address + - String : password : Account address password + +* **return value** + +```java +Request +``` + +The result in the PersonalSign attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +RawTransaction transaction = RawTransaction.createTransaction(nonce,gasPrice,gasLimit,toAddress,value,data); +byte[] encode = TransactionEncoder.encode(transaction); +String hexSignedTransaction = Numeric.toHexString(encode); +PersonalSign send = admin.personalSign(hexSignedTransaction, "atp1xxx","000000").send(); +``` + +### personalSignAndSendTransaction + +> Use the from address of the transaction to sign and send the transaction. The from account needs to exist in the node's account library + +* **parameters** + + - Transaction : transaction : Transaction object + - String : password : Account address password + +* **return value** + +```java +Request +``` + +The result in the PersonalSign attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PersonalSign send = admin.personalSignAndSendTransaction(transaction, "000000").send(); +``` + +### personalSignTransaction + +> Use the from address of the transaction to sign, and the from account needs to exist in the node's account database + +* **parameters** + + - Transaction : transaction : Transaction object + - String : password : From account address password + +* **return value** + +```java +Request +``` + +The result in the PlatonSignTransaction attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PlatonSignTransaction send = admin.personalSignTransaction(transaction,"000000").send(); +``` + +### personalEcRecover + +> Returns the address of the account used to create the signature + +* **parameters** + + - String : message : Raw data + - String : signiture : Signed data + +* **return value** + +```java +Request +``` + +The result in the PersonalEcRecover attribute is the corresponding storage data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalEcRecover send = admin.personalEcRecover("0xebxxx","0xa4f5xxx").send(); +``` + +### personalListWallets + +> Return the list of wallets managed by this node + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the PersonalListWallets attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalListWallets send = admin.personalListWallets().send(); +``` + +### personalOpenWallet + +> Start the hardware wallet opening program, establish a USB connection and try to authenticate with the provided password. Please note that this method may return an additional challenge that needs to be opened a second time (for example, Trezor PIN matrix challenge) + +* **parameters** + + - String : url : URL + - String : passphrase : password + +* **return value** + +```java +Request +``` + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +VoidResponse send = admin.personalOpenWallet("http://localhost:8080", "000000").send(); +``` + +### personalUnlockAccount + +> Use the given password to unlock the account associated with the given address for a duration of seconds. The default is 300 seconds. If the account is unlocked, it will return an indication + +* **parameters** + + - String : address : address + - String : passphrase : password + +* **return value** + +```java +Request +``` + +The result in the PersonalUnlockAccount attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalUnlockAccount response = admin.personalUnlockAccount("atp1xxx", "111111").send(); +``` + +### personalListAccounts + +> Returns a list of addresses of accounts managed by this node + +* **parameters** + + No + +* **return value** + +```java +Request +``` + +The result in the PersonalListAccounts attribute is the corresponding stored data + +* **Example** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalListAccounts send = admin.personalListAccounts().send(); +``` + ## System Contract Call diff --git a/doc/Java-SDK-zh.md b/doc/Java-SDK-zh.md index 1dc6654a..342b4bac 100644 --- a/doc/Java-SDK-zh.md +++ b/doc/Java-SDK-zh.md @@ -16,7 +16,7 @@ sidebar_label: Java SDK > 项目配置: ```xml - platon-public + alaya-public https://sdk.platon.network/nexus/content/groups/public/ ``` @@ -26,7 +26,7 @@ sidebar_label: Java SDK com.alaya.sdk core - 0.16.0.0 + 0.16.1.0 ``` @@ -41,7 +41,7 @@ repositories { > gradle引用方式: ``` -compile "com.alaya.sdk:core:0.16.0.0" +compile "com.alaya.sdk:core:0.16.1.0" ``` ## 基础api使用 @@ -252,6 +252,78 @@ Request request = currentValidWeb3j.netPeerCount(); BigInteger req = request.send().getQuantity(); ``` +### adminAddPeer + +> 新增一个端点到客户端节点 + +* **参数** + + String :端点URL + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminAddPeer("enode://0abaf3219f454f3d07b6cbcf3c10b6b4ccf605202868e2043b6f5db12b745df0604ef01ef4cb523adc6d9e14b83a76dd09f862e3fe77205d8ac83df707969b47@[::]:16789"); +Boolean resp = request.send().getResult(); +``` + +### adminRemovePeer + +> 从客户端节点移除一个端点 + +* **参数** + + String :端点URL + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminRemovePeer("enode://0abaf3219f454f3d07b6cbcf3c10b6b4ccf605202868e2043b6f5db12b745df0604ef01ef4cb523adc6d9e14b83a76dd09f862e3fe77205d8ac83df707969b47@[::]:16789"); +Boolean resp = request.send().getResult(); +``` + +### adminDataDir + +> 返回当前节点数据目录 + +* **参数** + + ​ 无 + +* **返回值** + +```java +Request +``` + +AdminDataDir属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.adminDataDir(); +String resp = request.send().getDataDir(); +``` + ### platonProtocolVersion > 返回当前platon协议版本 @@ -1153,6 +1225,54 @@ Request request = currentValidWeb3j.dbGetHex(databaseName,keyName) String req = request.send().getStoredValue(); ``` +### txPoolStatus + +> 返回交易池状态 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +TxPoolStatus属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.txPoolStatus(); +TxPoolStatus txPoolStatus = request.send(); +``` + +### txPoolContent + +> 返回交易池内容 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +TxPoolContent属性中的result即为对应存储数据 + +* **示例** + +```java +JsonRpc2_0Admin platonWeb3j = new JsonRpc2_0Admin(new HttpService("http://127.0.0.1:6789")); +Request request = platonWeb3j.txPoolContent(); +TxPoolContent txPoolContent = request.send(); +``` + ### platonEvidences > 返回双签举报数据 @@ -1397,6 +1517,631 @@ Request req = platonWeb3j.getChainId(); BigInteger chainId = req.send().getChainId(); ``` +### adminNodeInfo + +> 以协议粒度检索我们知道的有关主机节点的所有信息。 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +AdminNodeInfo属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminNodeInfo nodeInfo = platonWeb3j.adminNodeInfo().send(); +``` + +### adminPeers + +> 以协议粒度检索我们知道的关于每个单独 Peer 的所有信息 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +AdminPeers属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminPeers adminPeers = platonWeb3j.adminPeers().send(); +``` + +### adminStartRPC + +> 启动 HTTP RPC API 服务器 + +* **参数** + + - String : host : 要监听的网络地址 + - Integer : port : 要监听的网络端口 + - String : cors : 要使用的跨源资源共享头 + - String : apis : 要透过该服务接口提供服务的API模块 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStartRPC("127.0.0.1", 6789, null, null).send(); +``` + +### adminStartWS + +> 启动 websocket RPC API 服务器 + +* **参数** + + - String : host : 要监听的网络地址 + - Integer : port : 要监听的网络端口 + - String : cors : 要使用的跨源资源共享头 + - String : apis : 要透过该服务接口提供服务的API模块 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = web3j.adminStartWS("127.0.0.1", 7789, null, null).send(); +``` + +### adminStopRPC + +> 关闭当前启动的HTTP RPC端结点 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStopRPC().send(); +``` + +### adminStopWS + +> 关闭当前启动的WebSocket RPC端结点 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +WebSocketService webSocketService = new WebSocketService("ws://127.0.0.1:7790",false); +webSocketService.connect(); +Web3j platonWeb3j = Web3j.build(webSocketService); +BooleanResponse send = platonWeb3j.adminStopWS().send(); +``` + +### adminExportChain + +> 将当前区块链导出到本地文件中 + +* **参数** + + - String : file : 文件名 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.adminExportChain("1").send(); +``` + +### adminImportChain + +> 从本地文件导入区块链 + +* **参数** + + - String : file : 文件名 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.adminImportChain("1").send(); +``` + +### getWaitSlashingNodeList + +> 获取零出块的节点,因为零出块而被观察的节点列表 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +DebugWaitSlashingNodeList属性中的WaitSlashingNode列表对象即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Request req = platonWeb3j.getWaitSlashingNodeList(); +DebugWaitSlashingNodeList nodeList = req.send(); +``` + +### platonGetRawTransactionByHash + +> 返回给定Hash的交易字节数 + +* **参数** + + - String : hash : 交易hash + +* **返回值** + +```java +Request +``` + +PlatonRawTransaction属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByHash("0x5b99...").send(); +``` + +### platonGetRawTransactionByBlockHashAndIndex + +> 返回给定区块哈希和索引的交易 + +* **参数** + + - String : hash : 块hash + - String : index : 索引 + +* **返回值** + +```java +Request +``` + +PlatonRawTransaction属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByBlockHashAndIndex("0xa34...", "0x1").send(); +``` + +### platonGetRawTransactionByBlockNumberAndIndex + +> 返回给定区块号和索引的交易 + +* **参数** + + - String : blockNumber : 块号 + - String : index : 索引 + +* **返回值** + +```java +Request +``` + +PlatonRawTransaction属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonRawTransaction send = platonWeb3j.platonGetRawTransactionByBlockNumberAndIndex("0x1", "0x1").send(); +``` + +### platonGetAddressHrp + +> 获取链Hrp + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +PlatonGetAddressHrp属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +PlatonGetAddressHrp send = platonWeb3j.platonGetAddressHrp().send(); +``` + +### platonSignTransaction + +> 将使用 from 帐户签署给定的交易。节点需要有给定的发件人地址对应的账户私钥,并且需要解锁 + +* **参数** + + - Transaction : transaction : 交易对象 + +* **返回值** + +```java +Request +``` + +PlatonSignTransaction属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PlatonSignTransaction send = platonWeb3j.platonSignTransaction(transaction).send(); +``` + +### minerSetGasPrice + +> 设置矿工可接受的最低gas price + +* **参数** + + - String : minGasPrice : 最低gas price + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse send = platonWeb3j.minerSetGasPrice("0x1").send(); +``` + +### adminPeerEvents + +> 创建一个 RPC 订阅,它从节点的 p2p服务器接收 peer 事件 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +AdminPeerEvents属性中的result即为对应存储数据 + +* **示例** + +```java +Web3j platonWeb3j = Web3j.build(new HttpService("http://127.0.0.1:6789")); +AdminPeerEvents send = web3j.adminPeerEvents().send(); +``` + +### personalImportRawKey + +> 将给定的十六进制编码密钥存储到密钥目录中,并使用密码对其进行加密 + +* **参数** + + - String : keydata : 私钥 + - String : password : 密码 + +* **返回值** + +```java +Request +``` + +PersonalImportRawKey属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalImportRawKey key = admin.personalImportRawKey("03axxx", "000000").send(); +``` + +### personalLockAccount + +> 锁定与给定地址关联的帐户 + +* **参数** + + - String : address : 账户地址 + +* **返回值** + +```java +Request +``` + +BooleanResponse属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +BooleanResponse response = admin.personalLockAccount("atp1cxxx").send(); +``` + +### personalSign + +> 使用给定的账户对交易进行签名,账户需要存在于节点的账户库中 + +* **参数** + + - String : message : 交易的16进制编码字符串 + - String : accountId : 账户地址 + - String : password : 账户地址密码 + +* **返回值** + +```java +Request +``` + +PersonalSign属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +RawTransaction transaction = RawTransaction.createTransaction(nonce,gasPrice,gasLimit,toAddress,value,data); +byte[] encode = TransactionEncoder.encode(transaction); +String hexSignedTransaction = Numeric.toHexString(encode); +PersonalSign send = admin.personalSign(hexSignedTransaction, "atp1xxx","000000").send(); +``` + +### personalSignAndSendTransaction + +> 使用交易的from地址进行签名并发送交易,from账户需要存在于节点的账户库中 + +* **参数** + + - Transaction : transaction : 交易对象 + - String : password : 账户地址密码 + +* **返回值** + +```java +Request +``` + +PersonalSign属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PersonalSign send = admin.personalSignAndSendTransaction(transaction, "000000").send(); +``` + +### personalSignTransaction + +> 使用交易的from地址进行签名,from账户需要存在于节点的账户库中 + +* **参数** + + - Transaction : transaction : 交易对象 + - String : password : from账户地址密码 + +* **返回值** + +```java +Request +``` + +PlatonSignTransaction属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +Transaction transaction = new Transaction("atp1xxx",nonce,gasPrice,gasLimit,toAddress,value,data); +PlatonSignTransaction send = admin.personalSignTransaction(transaction,"000000").send(); +``` + +### personalEcRecover + +> 返回用于创建签名的帐户的地址 + +* **参数** + + - String : message : 原始数据 + - String : signiture : 签名后的数据 + +* **返回值** + +```java +Request +``` + +PersonalEcRecover属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalEcRecover send = admin.personalEcRecover("0xebxxx","0xa4f5xxx").send(); +``` + +### personalListWallets + +> 返回此节点管理的钱包列表 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +PersonalListWallets属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalListWallets send = admin.personalListWallets().send(); +``` + +### personalOpenWallet + +> 启动硬件钱包打开程序,建立 USB 连接并尝试通过提供的密码进行身份验证。请注意,该方法可能会返回一个需要第二次打开的额外质询(例如,Trezor PIN 矩阵质询) + +* **参数** + + - String : url : 网址 + - String : passphrase : 密码 + +* **返回值** + +```java +Request +``` + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +VoidResponse send = admin.personalOpenWallet("http://localhost:8080", "000000").send(); +``` + +### personalUnlockAccount + +> 使用给定密码解锁与给定地址关联的帐户持续时间秒。默认 300 秒。如果帐户已解锁,它会返回一个指示 + +* **参数** + + - String : address : 地址 + - String : passphrase : 密码 + +* **返回值** + +```java +Request +``` + +PersonalUnlockAccount属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalUnlockAccount response = admin.personalUnlockAccount("atp1xxx", "111111").send(); +``` + +### personalListAccounts + +> 返回此节点管理的帐户的地址列表 + +* **参数** + + 无 + +* **返回值** + +```java +Request +``` + +PersonalListAccounts属性中的result即为对应存储数据 + +* **示例** + +```java +Admin admin = Admin.build(new HttpService("http://127.0.0.1:6789")); +PersonalListAccounts send = admin.personalListAccounts().send(); +``` + ## 系统合约调用 系统接口主要包含经济模型和治理相关的合约接口: diff --git a/gradle.properties b/gradle.properties index 56048813..d48a1ed8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=com.alaya.sdk -version=0.16.0.0 +version=0.16.1.0 mavenReleases=https://sdk.platon.network/nexus/content/repositories/releases/ mavenSnapshots=https://sdk.platon.network/nexus/content/repositories/snapshots/ systemProp.sonar.host.url=http://192.168.16.173:9000 diff --git a/integration-tests/src/test/java/com/platon/protocol/core/JsonRpc2_0Web3jTest.java b/integration-tests/src/test/java/com/platon/protocol/core/JsonRpc2_0Web3jTest.java new file mode 100644 index 00000000..caeac57d --- /dev/null +++ b/integration-tests/src/test/java/com/platon/protocol/core/JsonRpc2_0Web3jTest.java @@ -0,0 +1,76 @@ +package com.platon.protocol.core; + +import com.platon.parameters.NetworkParameters; +import com.platon.protocol.Web3j; +import com.platon.protocol.core.methods.response.DebugWaitSlashingNodeList; +import com.platon.protocol.core.methods.response.PlatonChainId; +import com.platon.protocol.http.HttpService; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * @Author liushuyu + * @Date 2021/7/6 15:52 + * @Version + * @Desc + */ +public class JsonRpc2_0Web3jTest { + + private static final Logger logger = LoggerFactory.getLogger("JsonRpc2_0Web3jTest"); + + @Before + public void init(){ + NetworkParameters.selectPlatON(); + } + + /** + * 底层1.0.0版本 + * @throws IOException + */ + @Test + public void testChainId_version15_0_0() throws IOException { + Web3j web3j = Web3j.build(new HttpService("http://192.168.120.150:6789")); + PlatonChainId send = web3j.getChainId().send(); + Assert.assertTrue(send.getError() != null); + } + + /** + * 底层1.1.0版本 + * @throws IOException + */ + @Test + public void testChainId_version16_0_0() throws IOException { + Web3j web3j = Web3j.build(new HttpService("http://192.168.120.150:6780")); + PlatonChainId send = web3j.getChainId().send(); + Assert.assertTrue(send.getError() == null); + } + + /** + * 底层1.0.0版本 + * @throws IOException + */ + @Test + public void testGetWaitSlashingNodeList_version15_0_0() throws IOException { + Web3j web3j = Web3j.build(new HttpService("http://192.168.120.150:6789")); + DebugWaitSlashingNodeList nodeList = web3j.getWaitSlashingNodeList().send(); + Assert.assertTrue(nodeList.getResult() != null); + Assert.assertTrue(nodeList.getError() == null); + } + + /** + * 底层1.1.0版本 + * @throws IOException + */ + @Test + public void testGetWaitSlashingNodeList_version16_0_0() throws IOException { + Web3j web3j = Web3j.build(new HttpService("http://192.168.120.150:6780")); + DebugWaitSlashingNodeList nodeList = web3j.getWaitSlashingNodeList().send(); + Assert.assertTrue(nodeList.getResult() != null); + Assert.assertTrue(nodeList.getError() == null); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/platon/crypto/Hash.java b/utils/src/main/java/com/platon/crypto/Hash.java index 9eeea0fe..0bc583d7 100644 --- a/utils/src/main/java/com/platon/crypto/Hash.java +++ b/utils/src/main/java/com/platon/crypto/Hash.java @@ -1,6 +1,10 @@ package com.platon.crypto; import com.platon.utils.Numeric; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jcajce.provider.digest.Keccak; import java.nio.charset.StandardCharsets; @@ -74,4 +78,22 @@ public static byte[] sha256(byte[] input) { throw new RuntimeException("Couldn't find a SHA-256 provider", e); } } + + public static byte[] hmacSha512(byte[] key, byte[] input) { + HMac hMac = new HMac(new SHA512Digest()); + hMac.init(new KeyParameter(key)); + hMac.update(input, 0, input.length); + byte[] out = new byte[64]; + hMac.doFinal(out, 0); + return out; + } + + public static byte[] sha256hash160(byte[] input) { + byte[] sha256 = sha256(input); + RIPEMD160Digest digest = new RIPEMD160Digest(); + digest.update(sha256, 0, sha256.length); + byte[] out = new byte[20]; + digest.doFinal(out, 0); + return out; + } }