├── README.md ├── src ├── test │ └── resources │ │ ├── sample.sol │ │ └── SimpleStorage.sol └── main │ ├── java │ └── com │ │ └── mvc │ │ ├── polymorphic │ │ ├── model │ │ │ ├── dto │ │ │ │ ├── NewAccountDTO.java │ │ │ │ ├── TransactionByHashDTO.java │ │ │ │ ├── TransactionCountDTO.java │ │ │ │ ├── PersonalByPrivateKeyDTO.java │ │ │ │ ├── RawTransactionDTO.java │ │ │ │ ├── ExportAccountDTO.java │ │ │ │ ├── ImportRawKeyDTO.java │ │ │ │ ├── BalanceDTO.java │ │ │ │ └── SendTransactionDTO.java │ │ │ ├── Block.java │ │ │ ├── NodeConfiguration.java │ │ │ ├── Account.java │ │ │ ├── Filter.java │ │ │ ├── EthTransaction.java │ │ │ ├── JsonCredentials.java │ │ │ ├── TransferEventResponse.java │ │ │ ├── Method.java │ │ │ ├── TransactionResponse.java │ │ │ ├── ApprovalEventResponse.java │ │ │ ├── Receipt.java │ │ │ └── HumanStandardToken.java │ │ ├── exceptions │ │ │ └── ExceededGasException.java │ │ ├── service │ │ │ ├── EtherscanUrl.java │ │ │ ├── RpcService.java │ │ │ ├── EthService.java │ │ │ ├── RpcServiceImpl.java │ │ │ ├── BchService.java │ │ │ ├── BtcService.java │ │ │ └── ContractService.java │ │ ├── common │ │ │ ├── BlockResult.java │ │ │ ├── BlockException.java │ │ │ ├── BlockConfig.java │ │ │ ├── interceptor │ │ │ │ └── ServiceAuthRestInterceptor.java │ │ │ ├── SpringContextUtil.java │ │ │ ├── bean │ │ │ │ ├── BchTransaction.java │ │ │ │ └── BtcTransaction.java │ │ │ └── BlockChainService.java │ │ ├── utils │ │ │ ├── FileUtil.java │ │ │ ├── BlockServiceUtil.java │ │ │ ├── EthereumUtil.java │ │ │ ├── Denomination.java │ │ │ └── RSACoder.java │ │ ├── controller │ │ │ ├── DemoController.java │ │ │ ├── Controller.java │ │ │ ├── BlockController.java │ │ │ └── EthereumController.java │ │ └── configuration │ │ │ ├── TokenConfig.java │ │ │ ├── CorsConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ └── RpcConfiguration.java │ │ └── PolymorphicApplication.java │ └── resources │ ├── application-template.yml │ └── logback.xml ├── .gitignore ├── LICENSE └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # wallet demo 2 | about Ethereum 3 | -------------------------------------------------------------------------------- /src/test/resources/sample.sol: -------------------------------------------------------------------------------- 1 | contract sample { uint storedData = {0} ; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/NewAccountDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class NewAccountDTO { 7 | 8 | private String passphrase; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/TransactionByHashDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TransactionByHashDTO { 7 | private String transactionHash; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/TransactionCountDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TransactionCountDTO { 7 | 8 | private String address; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/PersonalByPrivateKeyDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class PersonalByPrivateKeyDTO { 7 | 8 | private String privateKey; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/RawTransactionDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RawTransactionDTO { 7 | 8 | private String signedMessage; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/ExportAccountDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ExportAccountDTO { 7 | private String address; 8 | private String passphrase; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/exceptions/ExceededGasException.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.exceptions; 2 | 3 | public class ExceededGasException extends Throwable { 4 | public ExceededGasException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/ImportRawKeyDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | 4 | import lombok.Data; 5 | 6 | @Data 7 | public class ImportRawKeyDTO { 8 | 9 | private String keydata; 10 | private String passphrase; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/service/EtherscanUrl.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.service; 2 | 3 | public interface EtherscanUrl { 4 | 5 | public final static String txlist = "?module=account&action=txlist&startblock=0&endblock=99999999&sort=asc&address=%s"; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/BalanceDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class BalanceDTO implements Serializable { 9 | private String address; 10 | private String blockId; 11 | } 12 | -------------------------------------------------------------------------------- /src/test/resources/SimpleStorage.sol: -------------------------------------------------------------------------------- 1 | contract SimpleStorage { 2 | uint storedData; 3 | function set(uint x) { 4 | storedData = x; 5 | } 6 | function get() constant returns (uint retVal) { 7 | return {0}; 8 | } 9 | 10 | function get1() constant returns (uint retVal) { 11 | return {1}; 12 | } 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .settings 3 | .classpath 4 | .project 5 | 6 | .idea/ 7 | *.iml 8 | *.ipr 9 | *.iws 10 | .gradle/ 11 | build/ 12 | mods/ 13 | .idea/libraries 14 | *.DS_Store 15 | application.yml 16 | 17 | *.class 18 | 19 | *_blockstore 20 | 21 | # Package Files 22 | # *.jar 23 | *.war 24 | *.ear 25 | 26 | # IDEA 27 | *.iml 28 | .idea/ 29 | .DS_Store 30 | local.* 31 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/Block.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | @Builder(builderClassName="Builder", toBuilder=true) 7 | @Setter 8 | @Getter 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @JsonIgnoreProperties(ignoreUnknown = true)public class Block { 12 | private String nonce; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/NodeConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * Node configuration bean. 9 | */ 10 | @Data 11 | @ConfigurationProperties 12 | @Component 13 | public class NodeConfiguration { 14 | 15 | private String nodeEndpoint; 16 | private String fromAddress; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/Account.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | @Builder(builderClassName="Builder", toBuilder=true) 7 | @Setter 8 | @Getter 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @JsonIgnoreProperties(ignoreUnknown = true) 12 | @ToString 13 | public class Account { 14 | private String passphrase; 15 | private String address; 16 | private long balance; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/BlockResult.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * block result 9 | * 10 | * @author qiyichen 11 | * @create 2018/4/9 16:22 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class BlockResult { 17 | private String type; 18 | private Boolean success; 19 | private Object error; 20 | private Object result; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/BlockException.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common; 2 | 3 | /** 4 | * BlockException 5 | * 6 | * @author qiyichen 7 | * @create 2018/4/9 17:09 8 | */ 9 | public class BlockException extends RuntimeException { 10 | 11 | private static final long serialVersionUID = -6526974425772871300L; 12 | 13 | 14 | public BlockException(String msg) { 15 | super(msg); 16 | } 17 | 18 | public BlockException(String msg, Exception e) { 19 | super(msg, e); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/Filter.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | @Builder(builderClassName="Builder", toBuilder=true) 7 | @Getter 8 | @Setter 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @JsonIgnoreProperties(ignoreUnknown = true) 12 | public class Filter { 13 | private String fromBlock = "0x0"; 14 | private String toBlock = "lastest"; 15 | private String address; 16 | private String topics; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/EthTransaction.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | 7 | @Builder(builderClassName="Builder", toBuilder=true) 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | public class EthTransaction { 14 | private String data; 15 | private String from; 16 | private String to; 17 | private long gas; 18 | private long gasPrice; 19 | private long value; 20 | // private long nonce; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/JsonCredentials.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import lombok.Data; 4 | import org.web3j.crypto.Credentials; 5 | 6 | @Data 7 | public class JsonCredentials { 8 | 9 | private String publicKey; 10 | private String privateKey; 11 | private String address; 12 | 13 | 14 | public JsonCredentials(Credentials credentials) { 15 | this.address = credentials.getAddress(); 16 | this.privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16); 17 | this.publicKey = credentials.getEcKeyPair().getPublicKey().toString(16); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/dto/SendTransactionDTO.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model.dto; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import java.io.Serializable; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * @author ethands 11 | */ 12 | @Data 13 | public class SendTransactionDTO implements Serializable { 14 | private static final long serialVersionUID = 6477321453043666156L; 15 | @NotNull 16 | private String pass; 17 | @NotNull 18 | private String from; 19 | @NotNull 20 | private String to; 21 | @NotNull 22 | private BigDecimal value; 23 | 24 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/TransferEventResponse.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TransferEventResponse { 7 | private String from; 8 | private String to; 9 | private long value; 10 | 11 | public TransferEventResponse() { 12 | } 13 | 14 | public TransferEventResponse( 15 | HumanStandardToken.TransferEventResponse transferEventResponse) { 16 | this.from = transferEventResponse._from.toString(); 17 | this.to = transferEventResponse._to.toString(); 18 | this.value = transferEventResponse._value.getValue().longValueExact(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/Method.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | import javax.validation.constraints.NotNull; 7 | 8 | @Builder(builderClassName="Builder", toBuilder=true) 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class Method { 15 | @NotNull 16 | private Object[] args; 17 | private String name; 18 | public Object[] getArgs() { 19 | if (args != null) { 20 | return args; 21 | } 22 | return new Object[0]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/TransactionResponse.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * TransactionResponse wrapper. 8 | */ 9 | @Getter 10 | @Setter 11 | public class TransactionResponse { 12 | 13 | private String transactionHash; 14 | private T event; 15 | 16 | TransactionResponse() { } 17 | 18 | public TransactionResponse(String transactionHash) { 19 | this(transactionHash, null); 20 | } 21 | 22 | public TransactionResponse(String transactionHash, T event) { 23 | this.transactionHash = transactionHash; 24 | this.event = event; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/ApprovalEventResponse.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ApprovalEventResponse { 7 | private String owner; 8 | private String spender; 9 | private long value; 10 | 11 | public ApprovalEventResponse() { } 12 | 13 | public ApprovalEventResponse( 14 | HumanStandardToken.ApprovalEventResponse approvalEventResponse) { 15 | this.owner = approvalEventResponse._owner.toString(); 16 | this.spender = approvalEventResponse._spender.toString(); 17 | this.value = approvalEventResponse._value.getValue().longValueExact(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/BlockConfig.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.math.BigInteger; 8 | 9 | /** 10 | * @author qiyichen 11 | * @create 2018/4/9 15:33 12 | */ 13 | @ConfigurationProperties( 14 | prefix = "mvc.block" 15 | )@Component 16 | @Data 17 | public class BlockConfig { 18 | 19 | public String ethService = "http://localhost:8545"; 20 | public Integer timeoutSec = 120; 21 | public BigInteger ethPrice = BigInteger.valueOf(45000); 22 | public BigInteger ethLimit = BigInteger.valueOf(45000); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | public class FileUtil { 9 | 10 | public static String readFile (InputStream inputStream) throws IOException { 11 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 12 | StringBuffer buffer = new StringBuffer(); 13 | String line = null; 14 | while ((line = bufferedReader.readLine()) != null){ 15 | buffer.append(line); 16 | } 17 | inputStream.close(); 18 | return buffer.toString(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/controller/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.controller; 2 | 3 | import com.mvc.polymorphic.configuration.TokenConfig; 4 | import io.swagger.annotations.Api; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @Api("Some Demos") 11 | @RestController 12 | @RequestMapping("/demo") 13 | public class DemoController { 14 | 15 | @Autowired 16 | private TokenConfig tokenConfig; 17 | 18 | @GetMapping("/config") 19 | public Object config() { 20 | return tokenConfig.getUrl(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/PolymorphicApplication.java: -------------------------------------------------------------------------------- 1 | package com.mvc; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | import org.springframework.scheduling.annotation.EnableScheduling; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | 12 | @Configuration 13 | @EnableAutoConfiguration 14 | @EnableScheduling 15 | @EnableSwagger2 16 | @EnableAsync 17 | @SpringBootApplication 18 | public class PolymorphicApplication { 19 | public static void main(String[] args) throws Exception { 20 | SpringApplication.run(PolymorphicApplication.class, args); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/interceptor/ServiceAuthRestInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common.interceptor; 2 | 3 | import com.mvc.tools.context.BaseContextHandler; 4 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * @author qyc 11 | */ 12 | public class ServiceAuthRestInterceptor extends HandlerInterceptorAdapter { 13 | 14 | @Override 15 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 16 | BaseContextHandler.remove(); 17 | super.afterCompletion(request, response, handler, ex); 18 | } 19 | 20 | @Override 21 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 22 | return super.preHandle(request, response, handler); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/configuration/TokenConfig.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.configuration; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.math.BigInteger; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | @Data 12 | @ConfigurationProperties(prefix = "mvc") 13 | @Component 14 | public class TokenConfig { 15 | 16 | public static final String ENV_LOCAL = "local"; 17 | public static final String ENV_TEST = "test"; 18 | public static final String ENV_PROD = "prod"; 19 | 20 | private Map env = new HashMap<>(); 21 | 22 | private Map> url = new HashMap<>(); 23 | private Map> path = new HashMap<>(); 24 | private Map> pass = new HashMap<>(); 25 | private Map>> gas = new HashMap<>(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 mvchain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/utils/BlockServiceUtil.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.utils; 2 | 3 | import com.mvc.tools.context.BaseContextHandler; 4 | 5 | /** 6 | * block service util 7 | * 8 | * @author qiyichen 9 | * @create 2018/4/9 17:24 10 | */ 11 | public class BlockServiceUtil { 12 | 13 | public enum BlockService { 14 | ETH("ETH", "EthService"), 15 | BTC("BTC", "BtcService"), 16 | BCH("BCH", "BchService"), 17 | ; 18 | private String type; 19 | private String serviceName; 20 | 21 | private BlockService(String type, String serviceName) { 22 | this.type = type; 23 | this.serviceName = serviceName; 24 | } 25 | 26 | public String getServiceName() { 27 | return serviceName; 28 | } 29 | 30 | public String getType() { 31 | return type; 32 | } 33 | 34 | public static String fromType(String type) { 35 | return valueOf(type).getServiceName(); 36 | } 37 | } 38 | 39 | public static String getServiceName(String type) { 40 | BaseContextHandler.set("type", type); 41 | return BlockService.fromType(type.toUpperCase()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/configuration/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.configuration; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.cors.CorsConfiguration; 7 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 8 | 9 | import java.util.Arrays; 10 | 11 | /** 12 | * 本机联调跨域问题 13 | */ 14 | @Configuration 15 | public class CorsConfig { 16 | 17 | @Value("${cors.allowedOrigin}") 18 | private String allowedOrigin; 19 | 20 | private CorsConfiguration buildConfig() { 21 | CorsConfiguration corsConfiguration = new CorsConfiguration(); 22 | corsConfiguration.setAllowedOrigins(Arrays.asList(allowedOrigin)); 23 | // corsConfiguration.setAllowedOrigin(allowedOrigin); // 1 24 | corsConfiguration.addAllowedHeader("*"); // 2 25 | corsConfiguration.addAllowedMethod("*"); // 3 26 | return corsConfiguration; 27 | } 28 | 29 | @Bean 30 | public org.springframework.web.filter.CorsFilter corsFilter() { 31 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 32 | source.registerCorsConfiguration("/**", buildConfig()); // 4 33 | return new org.springframework.web.filter.CorsFilter(source); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/service/RpcService.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.service; 2 | 3 | import org.web3j.protocol.core.methods.request.Transaction; 4 | 5 | import java.io.IOException; 6 | import java.util.concurrent.ExecutionException; 7 | 8 | /** 9 | * remote service 10 | */ 11 | public interface RpcService { 12 | 13 | Object eth_getBalance(String address, String blockId) throws Exception; 14 | 15 | Object eth_getTransactionByHash(String transactionHash) throws Exception; 16 | 17 | Object eth_sendTransaction(Transaction transaction, String pass) throws Exception; 18 | 19 | Object personal_newAccount(String passhphrase) throws Exception; 20 | 21 | Object eth_sendTransaction(Transaction transaction, String pass, String contractAddress) throws Exception; 22 | 23 | Object personal_listAccounts() throws IOException; 24 | 25 | Object personal_importRawKey(String keydata, String passphrase) throws Exception; 26 | 27 | Object parityExportAccount(String address, String passphrase) throws IOException; 28 | 29 | Object ethSendRawTransaction(String signedMessage) throws Exception; 30 | 31 | Object getTransactionCount(String address) throws ExecutionException, InterruptedException; 32 | 33 | Object eth_personalByKeyDate(String source, String passhphrase) throws Exception; 34 | 35 | Object eth_personalByPrivateKey(String privateKey) throws Exception; 36 | 37 | Object txList(String address); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/configuration/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.configuration; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.service.ApiInfo; 9 | import springfox.documentation.spi.DocumentationType; 10 | import springfox.documentation.spring.web.plugins.Docket; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | @Configuration 14 | @EnableSwagger2 15 | public class SwaggerConfig { 16 | 17 | @Bean 18 | public Docket createRestApi() { 19 | return new Docket(DocumentationType.SWAGGER_2) 20 | .apiInfo(apiInfo()) 21 | .select() 22 | .apis(RequestHandlerSelectors.basePackage("com.mvc.polymorphic")) 23 | .paths(PathSelectors.any()) 24 | .build(); 25 | } 26 | 27 | private ApiInfo apiInfo() { 28 | return new ApiInfoBuilder() 29 | .title("MVC Polymorphic Wallet APIs") 30 | .description("For more:") 31 | .termsOfServiceUrl("http://www.mvchain.net") 32 | .contact("") 33 | .version("1.0") 34 | .build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/utils/EthereumUtil.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.math.BigInteger; 7 | import java.util.stream.Collectors; 8 | 9 | public class EthereumUtil { 10 | 11 | public static String adapt(final String content) { 12 | return content.replace("\n", "").replace("\r", ""); 13 | } 14 | 15 | public static String removeLineBreaksFromFile(final String file, final Class clazz) { 16 | InputStream stream = clazz.getClassLoader().getResourceAsStream(file); 17 | BufferedReader buffer = new BufferedReader(new InputStreamReader(stream)); 18 | String collect = buffer.lines().collect(Collectors.joining("\n")); 19 | return collect.replace("\n", "").replace("\r", ""); 20 | } 21 | 22 | public static String adapt(final String content, final int arguments) { 23 | String collect = content.replace("\n", "") 24 | .replaceAll("\\{", "'{'").replaceAll("\\}", "'}'"); 25 | for(int i = 0; i < arguments; i++) { 26 | collect = collect.replaceAll("'\\{'"+ i +"'\\}'", "\\{" + i + "\\}"); 27 | } 28 | return collect.replaceAll("''",""); 29 | } 30 | 31 | public static Long decryptQuantity(String quantity) { 32 | BigInteger latestBalance = new BigInteger( 33 | "00" + quantity.substring(2), 16); 34 | return latestBalance.longValue(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/configuration/RpcConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.mvc.polymorphic.configuration; 5 | 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.web.client.RestTemplate; 12 | import org.web3j.protocol.Web3j; 13 | import org.web3j.protocol.admin.Admin; 14 | import org.web3j.protocol.geth.Geth; 15 | import org.web3j.protocol.http.HttpService; 16 | import org.web3j.quorum.Quorum; 17 | 18 | @Configuration 19 | @EnableConfigurationProperties 20 | @Slf4j 21 | public class RpcConfiguration { 22 | 23 | @Value("${org.ethereum.address}") 24 | private String ethereumAddress; 25 | 26 | @Bean 27 | public RestTemplate restTemplate() { 28 | return new RestTemplate(); 29 | } 30 | 31 | @Bean 32 | public Web3j web3j() { 33 | return Web3j.build(new HttpService(ethereumAddress)); 34 | } 35 | 36 | @Bean 37 | public Admin admin() { 38 | 39 | return Admin.build(new HttpService(ethereumAddress)); 40 | } 41 | 42 | @Bean 43 | public Geth geth() { 44 | return Geth.build(new HttpService(ethereumAddress)); 45 | } 46 | 47 | @Bean 48 | public Quorum quorum() { 49 | return Quorum.build(new HttpService(ethereumAddress)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common; 2 | 3 | import com.mvc.polymorphic.common.interceptor.ServiceAuthRestInterceptor; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.beans.factory.BeanFactory; 6 | import org.springframework.beans.factory.BeanFactoryAware; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.servlet.config.annotation.InterceptorRegistration; 10 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 12 | 13 | @Component 14 | @Configuration 15 | public class SpringContextUtil implements BeanFactoryAware, WebMvcConfigurer { 16 | private static BeanFactory beanFactory; 17 | 18 | @Override 19 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 20 | SpringContextUtil.beanFactory = beanFactory; 21 | } 22 | 23 | @Override 24 | public void addInterceptors(InterceptorRegistry registry) { 25 | InterceptorRegistration addInterceptor = registry.addInterceptor(new ServiceAuthRestInterceptor()); 26 | // 排除配置 27 | addInterceptor.excludePathPatterns("/error"); 28 | addInterceptor.excludePathPatterns("/login**"); 29 | // 拦截配置 30 | addInterceptor.addPathPatterns("/**"); 31 | } 32 | 33 | public static T getBean(String beanName) { 34 | if (null != beanFactory) { 35 | return (T) beanFactory.getBean(beanName); 36 | } 37 | return null; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/model/Receipt.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.mvc.polymorphic.utils.EthereumUtil; 5 | import lombok.*; 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | import java.io.Serializable; 9 | 10 | @Builder(builderClassName="Builder", toBuilder=true) 11 | @Setter 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | @ToString(of= {"contractAddress", "accountAddr"}, includeFieldNames = false) 16 | public class Receipt implements Serializable { 17 | @Getter 18 | private String transactionHash; 19 | @Getter 20 | private String contractAddress; 21 | @Getter 22 | private String blockHash; 23 | private String transactionIndex; 24 | private String blockNumber; 25 | private String cumulativeGasUsed; 26 | private String gasUsed; 27 | private Type type; 28 | @Getter 29 | @Setter 30 | private String accountAddr; 31 | 32 | public enum Type {CREATE, MODIFY}; 33 | 34 | public long getTransactionIndex() { 35 | return decrypt(transactionIndex); 36 | } 37 | 38 | public long getBlockNumber() { 39 | return decrypt(blockNumber); 40 | } 41 | 42 | public long getCumulativeGasUsed() { 43 | return decrypt(cumulativeGasUsed); 44 | } 45 | 46 | public long getGasUsed() { 47 | return decrypt(gasUsed); 48 | } 49 | 50 | private static long decrypt(final String data) { 51 | if (StringUtils.isNotBlank(data)) { 52 | return EthereumUtil.decryptQuantity(data); 53 | } 54 | return 0; 55 | 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/utils/Denomination.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.utils; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | 6 | /** 7 | * 官方包下的转换方法使用了BigInteger, 导致转化后小数被抹去, 这里换成了BigDecimal 8 | */ 9 | public enum Denomination { 10 | WEI(newBigDecimal(0)), 11 | SZABO(newBigDecimal(12)), 12 | FINNEY(newBigDecimal(15)), 13 | ETHER(newBigDecimal(18)); 14 | 15 | private BigDecimal amount; 16 | 17 | private Denomination(BigDecimal value) { 18 | this.amount = value; 19 | } 20 | 21 | public BigDecimal value() { 22 | return this.amount; 23 | } 24 | 25 | public long longValue() { 26 | return this.value().longValue(); 27 | } 28 | 29 | private static BigDecimal newBigDecimal(int value) { 30 | return BigDecimal.valueOf(10L).pow(value); 31 | } 32 | 33 | public static String toFriendlyString(BigInteger value) { 34 | BigDecimal decimal = new BigDecimal(value); 35 | if (decimal.compareTo(ETHER.value()) != 1 && decimal.compareTo(ETHER.value()) != 0) { 36 | if (decimal.compareTo(FINNEY.value()) != 1 && decimal.compareTo(FINNEY.value()) != 0) { 37 | return decimal.compareTo(SZABO.value()) != 1 && decimal.compareTo(SZABO.value()) != 0 ? Float.toString(decimal.divide(WEI.value()).floatValue()) + " WEI" : Float.toString(decimal.divide(SZABO.value()).floatValue()) + " SZABO"; 38 | } else { 39 | return Float.toString(decimal.divide(FINNEY.value()).floatValue()) + " FINNEY"; 40 | } 41 | } else { 42 | return Float.toString(decimal.divide(ETHER.value()).floatValue()) + " ETHER"; 43 | } 44 | } 45 | 46 | public static BigInteger getFriendlyValue(BigDecimal value){ 47 | return value.multiply(ETHER.value()).toBigInteger(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/bean/BchTransaction.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common.bean; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.mvc.bitcoincashj.core.Transaction; 5 | import com.mvc.bitcoincashj.wallet.Wallet; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * BtcTransaction 14 | * 15 | * @author qiyichen 16 | * @create 2018/3/1 11:43 17 | */ 18 | @Data 19 | public class BchTransaction implements Serializable { 20 | private static final long serialVersionUID = -8380806472534356994L; 21 | private String hash; 22 | private Date updatedAt; 23 | private Long value; 24 | private String valueStr; 25 | private String feeStr; 26 | private Long fee; 27 | private Long version; 28 | private Integer depth; 29 | private String fromAddress; 30 | private String toAddress; 31 | 32 | public static BchTransaction build(Transaction trans, Wallet wallet) { 33 | BchTransaction transaction = new BchTransaction(); 34 | transaction.setHash(trans.getHashAsString()); 35 | transaction.setFeeStr(null == trans.getFee() ? "0" : trans.getFee().toFriendlyString()); 36 | transaction.setFee(null == trans.getFee() ? 0 : trans.getFee().getValue()); 37 | transaction.setValueStr(trans.getValue(wallet).toFriendlyString()); 38 | transaction.setVersion(trans.getVersion()); 39 | String from = JSON.toJSONString(trans.getInputs().stream().map(obj -> obj.getFromAddress().toString()).collect(Collectors.toList())); 40 | transaction.setFromAddress(from); 41 | String to = JSON.toJSONString(trans.getOutputs().stream().map(obj -> obj.getAddressFromP2PKHScript(wallet.getParams()).toString()).collect(Collectors.toList())); 42 | transaction.setToAddress(to); 43 | transaction.setDepth(trans.getConfidence().getDepthInBlocks()); 44 | transaction.setValue(trans.getValue(wallet).getValue()); 45 | transaction.setUpdatedAt(trans.getUpdateTime()); 46 | return transaction; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/application-template.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 24842 3 | 4 | logging: 5 | level: 6 | com.mvc.ethereum: DEBUG 7 | com.mvc.bitcoincashj: ERROR 8 | org.bitcoinj: ERROR 9 | 10 | mvc: 11 | env: 12 | # all property has the preference over any other config bellow. 13 | all: local 14 | btc: local 15 | eth: local 16 | xrp: local 17 | bch: local 18 | ltc: local 19 | eos: local 20 | ada: local 21 | bcx: local 22 | xlm: local 23 | neo: local 24 | iota: local 25 | xmr: local 26 | dash: local 27 | trx: local 28 | usdt: local 29 | xem: local 30 | bnb: local 31 | etc: local 32 | ven: local 33 | xbg: local 34 | qtum: local 35 | omg: local 36 | ont: local 37 | url: 38 | eth: 39 | local: http://127.0.0.1:8545 40 | test: http://127.0.0.1:8545 41 | prod: http://127.0.0.1:8545 42 | path: 43 | btc: 44 | local: 'E:\bitblock\bitcoinj-blocks\' 45 | test: '\opt\bitblock\bitcoinj-blocks\' 46 | prod: '\opt\bitblock\bitcoinj-blocks\' 47 | bch: 48 | local: 'E:\bitblock\bitcoincashj-blocks\' 49 | test: '\opt\bitblock\bitcoincashj-blocks\' 50 | prod: '\opt\bitblock\bitcoincashj-blocks\' 51 | pass: 52 | btc: 53 | local: 123456 54 | test: 123456 55 | prod: 123456 56 | bch: 57 | local: 123456 58 | test: 123456 59 | prod: 123456 60 | gas: 61 | eth: 62 | local: 63 | limit: 4300000 64 | price: 22000000000 65 | test: 66 | limit: 4300000 67 | price: 22000000000 68 | prod: 69 | limit: 430000 70 | price: 2200000000 71 | 72 | org.ethereum: 73 | # address: http://192.168.206.229:8545 74 | address: http://127.0.0.1:8545 75 | 76 | 77 | cors: 78 | # allowedOrigin: '*' 79 | allowedOrigin: '*' 80 | 81 | swagger: 82 | enabled: false 83 | title: mvc-polymorphic-api 84 | description: mvc-polymorphic-api 85 | base-package: com.mvc.polymorphic.controller 86 | base-path: /** 87 | exclude-path: /error,/ops/** 88 | version: @project.version@ 89 | 90 | trans.log.url: http://etherscan.io/api -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/bean/BtcTransaction.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common.bean; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.Data; 5 | import org.bitcoinj.core.Transaction; 6 | import org.bitcoinj.wallet.Wallet; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * BtcTransaction 14 | * 15 | * @author qiyichen 16 | * @create 2018/3/1 11:43 17 | */ 18 | @Data 19 | public class BtcTransaction implements Serializable { 20 | private static final long serialVersionUID = -8380806472534356994L; 21 | private String hash; 22 | private Date updatedAt; 23 | private Long value; 24 | private String valueStr; 25 | private String feeStr; 26 | private Long fee; 27 | private Long version; 28 | private Integer depth; 29 | private String fromAddress; 30 | private String toAddress; 31 | 32 | public static BtcTransaction build(Transaction trans, Wallet wallet) { 33 | BtcTransaction transaction = new BtcTransaction(); 34 | transaction.setHash(trans.getHashAsString()); 35 | transaction.setFeeStr(null == trans.getFee() ? "0" : trans.getFee().toFriendlyString()); 36 | transaction.setFee(null == trans.getFee() ? 0 : trans.getFee().getValue()); 37 | transaction.setValueStr(trans.getValue(wallet).toFriendlyString()); 38 | transaction.setVersion(trans.getVersion()); 39 | // lamda expression, transform input streams to a list, then to a JSON string. 40 | String from = JSON.toJSONString(trans.getInputs().stream().map(obj -> obj.getFromAddress().toString()).collect(Collectors.toList())); 41 | transaction.setFromAddress(from); 42 | String to = JSON.toJSONString(trans.getOutputs().stream().map(obj -> obj.getAddressFromP2PKHScript(wallet.getParams()).toString()).collect(Collectors.toList())); 43 | transaction.setToAddress(to); 44 | // conformation count 45 | transaction.setDepth(trans.getConfidence().getDepthInBlocks()); 46 | transaction.setValue(trans.getValue(wallet).getValue()); 47 | transaction.setUpdatedAt(trans.getUpdateTime()); 48 | return transaction; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/controller/Controller.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.controller; 2 | 3 | import com.mvc.polymorphic.model.dto.BalanceDTO; 4 | import com.mvc.polymorphic.model.dto.TransactionCountDTO; 5 | import com.mvc.polymorphic.service.ContractService; 6 | import com.mvc.polymorphic.service.RpcService; 7 | import io.swagger.annotations.Api; 8 | import io.swagger.annotations.ApiOperation; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | /** 13 | * Controller for our ERC-20 contract API. 14 | */ 15 | @Api("ERC-20 token standard API") 16 | @RestController 17 | public class Controller { 18 | @Autowired 19 | private ContractService ContractService; 20 | 21 | @Autowired 22 | private RpcService rpcService; 23 | 24 | @ApiOperation("Get token balance for address") 25 | @RequestMapping( 26 | value = "/{contractAddress}/eth_getBalance", method = RequestMethod.POST) 27 | public Object balanceOf( 28 | @PathVariable String contractAddress, 29 | @RequestBody final BalanceDTO balanceDTO) { 30 | return ContractService.balanceOf(contractAddress, balanceDTO.getAddress()); 31 | } 32 | 33 | // @RequestMapping(value = "/{contractAddress}/eth_sendTransaction", method = RequestMethod.POST) 34 | // public Object approveAndCall(@PathVariable String contractAddress, @RequestBody SendTransactionDTO sendTransactionDTO) throws Exception { 35 | // Transaction transaction = new Transaction(sendTransactionDTO.getFrom(), sendTransactionDTO.getNonce(), sendTransactionDTO.getGasPrice(), sendTransactionDTO.getGas(), sendTransactionDTO.getTo(), 36 | // sendTransactionDTO.getValue().toBigInteger(), 37 | // sendTransactionDTO.getData()); 38 | // return rpcService.eth_sendTransaction(transaction, sendTransactionDTO.getPass(), contractAddress); 39 | // } 40 | 41 | @RequestMapping(value = "/{contractAddress}/txList", method = RequestMethod.POST) 42 | private Object txList(@PathVariable String contractAddress, @RequestBody TransactionCountDTO transactionCountDTO) { 43 | return rpcService.txList(transactionCountDTO.getAddress()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | logs/ethereum.log 6 | 7 | 8 | DEBUG 9 | 10 | 11 | 12 | true 13 | 14 | %date{HH:mm:ss.SSS} %-5level [%logger{35}] %msg%n 15 | 16 | 17 | 18 | logs/ethereum-%d{yyyy-MM-dd_HH}.%i.log 19 | 20 | 100MB 21 | 22 | 30 23 | 24 | 25 | 26 | 27 | 28 | 29 | INFO 30 | 31 | 32 | 33 | %date{HH:mm:ss.SSS} %-5level [%logger{35}] %msg%n 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/common/BlockChainService.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.common; 2 | 3 | import com.mvc.polymorphic.configuration.TokenConfig; 4 | import com.mvc.tools.context.BaseContextHandler; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.math.BigDecimal; 10 | 11 | /** 12 | * @author ethands 13 | */ 14 | @Service 15 | public abstract class BlockChainService implements CommandLineRunner { 16 | 17 | @Autowired 18 | protected TokenConfig tokenConfig; 19 | @Autowired 20 | protected BlockConfig blockConfig; 21 | 22 | private BlockChainService getService(String serviceName) { 23 | BlockChainService service = SpringContextUtil.getBean(serviceName); 24 | if (null == service) { 25 | throw new BlockException(String.format("%s is not found", serviceName)); 26 | } 27 | return service; 28 | } 29 | 30 | public BlockResult getBalance(String serviceName, String address) throws Exception { 31 | return getService(serviceName).getBalance(address); 32 | } 33 | 34 | protected abstract BlockResult getBalance(String address) throws Exception; 35 | 36 | public BlockResult getTransactionByHash(String serviceName, String transactionHash) throws Exception { 37 | return getService(serviceName).getTransactionByHash(transactionHash); 38 | } 39 | 40 | protected abstract BlockResult getTransactionByHash(String transactionHash) throws Exception; 41 | 42 | public BlockResult sendTransaction(String serviceName, String pass, String from, String to, BigDecimal value) throws Exception { 43 | return getService(serviceName).sendTransaction(pass, from, to, value); 44 | } 45 | 46 | protected abstract BlockResult sendTransaction(String pass, String from, String to, BigDecimal value) throws Exception; 47 | 48 | public BlockResult newAccount(String serviceName, String pass) { 49 | return getService(serviceName).newAccount(pass); 50 | } 51 | 52 | protected abstract BlockResult newAccount(String pass); 53 | 54 | public BlockResult getConfirmation(String serviceName, String transactionHash) throws Exception { 55 | return getService(serviceName).getConfirmation(transactionHash); 56 | } 57 | 58 | protected abstract BlockResult getConfirmation(String transactionHash) throws Exception; 59 | 60 | protected abstract void onTransaction(Object... objects); 61 | 62 | protected BlockResult tokenSuccess(String tokenName, Object result) { 63 | return new BlockResult(tokenName, true, null, result); 64 | } 65 | 66 | protected BlockResult tokenFail(String tokenName, String msg) { 67 | return new BlockResult(tokenName, false, msg, null); 68 | } 69 | 70 | protected String getType() { 71 | return (String) BaseContextHandler.get("type"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/controller/BlockController.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.controller; 2 | 3 | import com.mvc.polymorphic.common.BlockChainService; 4 | import com.mvc.polymorphic.common.BlockResult; 5 | import com.mvc.polymorphic.model.dto.NewAccountDTO; 6 | import com.mvc.polymorphic.model.dto.SendTransactionDTO; 7 | import com.mvc.polymorphic.utils.BlockServiceUtil; 8 | import com.mvc.tools.controller.BaseController; 9 | import com.mvc.tools.pojo.Result; 10 | import io.swagger.annotations.ApiOperation; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | 16 | /** 17 | * block controller 18 | * 19 | * @author qiyichen 20 | * @create 2018/4/9 17:13 21 | */ 22 | @RestController 23 | @RequestMapping("/token") 24 | public class BlockController extends BaseController { 25 | 26 | @Autowired 27 | private BlockChainService blockChainService; 28 | 29 | @ApiOperation(value = "New Account", notes = "Currently, the address is useless for BTC and BCH.") 30 | @GetMapping("/{type}/{address}") 31 | public Result getBalance(@PathVariable String type, @PathVariable String address) throws Exception { 32 | String serviceName = BlockServiceUtil.getServiceName(type); 33 | BlockResult result = blockChainService.getBalance(serviceName, address); 34 | return success(result); 35 | } 36 | 37 | @GetMapping("/{type}/hash/{hash}") 38 | public Result getTransactionByHash(@PathVariable String type, @PathVariable String hash) throws Exception { 39 | String serviceName = BlockServiceUtil.getServiceName(type); 40 | BlockResult result = blockChainService.getTransactionByHash(serviceName, hash); 41 | return success(result); 42 | } 43 | 44 | @GetMapping("/{type}/confirmation/{hash}") 45 | public Result getConfirmation(@PathVariable String type, @PathVariable String hash) throws Exception { 46 | String serviceName = BlockServiceUtil.getServiceName(type); 47 | BlockResult result = blockChainService.getConfirmation(serviceName, hash); 48 | return success(result); 49 | } 50 | 51 | @ApiOperation(value = "New Account", notes = "Currently, the password is useless for BTC and BCH.") 52 | @PostMapping("/{type}/account") 53 | public Result newAccount(@PathVariable String type, @Valid @RequestBody NewAccountDTO newAccountDTO) { 54 | String serviceName = BlockServiceUtil.getServiceName(type); 55 | BlockResult result = blockChainService.newAccount(serviceName, newAccountDTO.getPassphrase()); 56 | return success(result); 57 | } 58 | 59 | @PostMapping("/{type}/transaction") 60 | public Result sendTransaction(@PathVariable String type, @Valid @RequestBody SendTransactionDTO sendTransactionDTO) throws Exception { 61 | String serviceName = BlockServiceUtil.getServiceName(type); 62 | BlockResult result = blockChainService.sendTransaction(serviceName, sendTransactionDTO.getPass(), sendTransactionDTO.getFrom(), sendTransactionDTO.getTo(), sendTransactionDTO.getValue()); 63 | return success(result); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/service/EthService.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.service; 2 | 3 | import com.mvc.polymorphic.common.BlockChainService; 4 | import com.mvc.polymorphic.common.BlockException; 5 | import com.mvc.polymorphic.common.BlockResult; 6 | import com.mvc.polymorphic.configuration.TokenConfig; 7 | import lombok.extern.java.Log; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.util.Assert; 11 | import org.springframework.util.StringUtils; 12 | import org.web3j.protocol.Web3j; 13 | import org.web3j.protocol.admin.Admin; 14 | import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount; 15 | import org.web3j.protocol.core.DefaultBlockParameterName; 16 | import org.web3j.protocol.core.methods.response.EthGetBalance; 17 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 18 | import org.web3j.protocol.core.methods.response.EthTransaction; 19 | import org.web3j.protocol.geth.Geth; 20 | import org.web3j.quorum.Quorum; 21 | import org.web3j.utils.Convert; 22 | 23 | import java.math.BigDecimal; 24 | import java.math.BigInteger; 25 | 26 | import static org.web3j.utils.Convert.fromWei; 27 | 28 | @Service(value = "EthService") 29 | @Log 30 | public class EthService extends BlockChainService { 31 | 32 | public static final String symbol = "eth"; 33 | 34 | private String gethUrl; 35 | 36 | private BigInteger gasLimit; 37 | 38 | private BigInteger gasPrice; 39 | 40 | @Autowired 41 | private TokenConfig tokenConfig; 42 | 43 | @Autowired 44 | private Admin admin; 45 | 46 | @Autowired 47 | private Geth geth; 48 | 49 | @Autowired 50 | private Web3j web3j; 51 | 52 | @Autowired 53 | private Quorum quorum; 54 | 55 | @Override 56 | protected BlockResult getBalance(String address)throws Exception { 57 | EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send(); 58 | BlockResult blockResult = new BlockResult(symbol, true, null, 59 | fromWei(String.valueOf(ethGetBalance.getBalance()), Convert.Unit.ETHER)); 60 | return blockResult; 61 | } 62 | 63 | @Override 64 | protected BlockResult getTransactionByHash(String transactionHash) throws Exception { 65 | EthTransaction ethTransaction = web3j.ethGetTransactionByHash(transactionHash).send(); 66 | BlockResult blockResult = new BlockResult(symbol, true, null, ethTransaction); 67 | return blockResult; 68 | } 69 | 70 | @Override 71 | protected BlockResult sendTransaction(String pass, String from, String to, BigDecimal value) throws Exception { 72 | PersonalUnlockAccount flag = admin.personalUnlockAccount(from, pass).send(); 73 | if (flag.getError() != null) throw new BlockException("Eth send - " + flag.getError().getMessage()); 74 | Assert.isTrue(flag.accountUnlocked(), String.format("Account unlock error: %s", from)); 75 | org.web3j.protocol.core.methods.request.Transaction transaction = new org.web3j.protocol.core.methods.request.Transaction( 76 | from, 77 | null, 78 | gasLimit, 79 | gasPrice, 80 | to, 81 | Convert.toWei(value, Convert.Unit.ETHER).toBigInteger(), 82 | null 83 | ); 84 | EthSendTransaction ethSendTransaction = web3j.ethSendTransaction(transaction).send(); 85 | BlockResult blockResult = new BlockResult(symbol, true, null, ethSendTransaction); 86 | return blockResult; 87 | } 88 | 89 | @Override 90 | protected BlockResult newAccount(String pass) { 91 | return null; 92 | } 93 | 94 | @Override 95 | protected BlockResult getConfirmation(String transactionHash) throws Exception { 96 | BigInteger receiptBlockNumber = 97 | web3j.ethGetTransactionReceipt(transactionHash).send().getTransactionReceipt().get().getBlockNumber(); 98 | BigInteger latestBlockNumber = web3j.ethBlockNumber().send().getBlockNumber(); 99 | // blocks on top. 100 | BigInteger confirmationCount = latestBlockNumber.subtract(receiptBlockNumber).add(BigInteger.ONE); 101 | BlockResult blockResult = new BlockResult(symbol, true, null, confirmationCount); 102 | return blockResult; 103 | } 104 | 105 | @Override 106 | protected void onTransaction(Object... objects) { 107 | 108 | } 109 | 110 | @Override 111 | public void run(String... args) throws Exception { 112 | if (!StringUtils.isEmpty(tokenConfig.getEnv().get(symbol))) { 113 | String allEnv = tokenConfig.getEnv().get("all"); 114 | String env = allEnv != null ? allEnv : tokenConfig.getEnv().get(symbol); 115 | gethUrl = tokenConfig.getUrl().get(symbol).get(env); 116 | gasLimit = tokenConfig.getGas().get(symbol).get(env).get("limit"); 117 | gasPrice = tokenConfig.getGas().get(symbol).get(env).get("price"); 118 | log.info("ETH Service initialized and geth Url is :" + gethUrl); 119 | } else { 120 | log.info("ETH not supported!"); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/controller/EthereumController.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.controller; 2 | 3 | import com.mvc.polymorphic.model.dto.*; 4 | import com.mvc.polymorphic.service.RpcService; 5 | import com.mvc.polymorphic.utils.FileUtil; 6 | import com.mvc.polymorphic.utils.RSACoder; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | 13 | /** 14 | * all demo in this controller 15 | */ 16 | @RestController 17 | @RequestMapping("ethereum") 18 | public class EthereumController { 19 | 20 | @Autowired 21 | private RpcService rpcService; 22 | 23 | /** 24 | * getBalance 25 | * 26 | * @param balanceDTO 27 | * @return 28 | */ 29 | @RequestMapping(value = "eth_getBalance", method = RequestMethod.POST) 30 | public Object eth_getBalance(HttpServletRequest request, @RequestBody final BalanceDTO balanceDTO) throws Exception { 31 | return rpcService.eth_getBalance(balanceDTO.getAddress(), balanceDTO.getBlockId()); 32 | } 33 | 34 | /** 35 | * getTransactionByHash 36 | * 37 | * @return 38 | */ 39 | @RequestMapping(value = "eth_getTransactionByHash", method = RequestMethod.POST) 40 | public Object eth_getTransactionByHash(@RequestBody TransactionByHashDTO transactionByHashDTO) throws Exception { 41 | return rpcService.eth_getTransactionByHash(transactionByHashDTO.getTransactionHash()); 42 | } 43 | 44 | /** 45 | * sendRawTransaction 46 | * 47 | * @param rawTransactionDTO 48 | * @return 49 | * @throws Exception 50 | */ 51 | @RequestMapping(value = "eth_sendRawTransaction", method = RequestMethod.POST) 52 | public Object ethSendRawTransaction(@RequestBody RawTransactionDTO rawTransactionDTO) throws Exception { 53 | return rpcService.ethSendRawTransaction(rawTransactionDTO.getSignedMessage()); 54 | } 55 | 56 | /** 57 | * sendTransaction 58 | * 59 | * @param sendTransactionDTO 60 | * @return 61 | * @throws Exception 62 | */ 63 | // @RequestMapping(value = "eth_sendTransaction", method = RequestMethod.POST) 64 | // public Object eth_sendTransaction(@RequestBody SendTransactionDTO sendTransactionDTO) throws Exception { 65 | // Transaction transaction = new Transaction(sendTransactionDTO.getFrom(), sendTransactionDTO.getNonce(), sendTransactionDTO.getGasPrice(), sendTransactionDTO.getGas(), sendTransactionDTO.getTo(), 66 | // Convert.toWei(sendTransactionDTO.getValue(), Convert.Unit.ETHER).toBigInteger(), 67 | // sendTransactionDTO.getData()); 68 | // return rpcService.eth_sendTransaction(transaction, sendTransactionDTO.getPass()); 69 | // } 70 | 71 | /** 72 | * search user list 73 | * 74 | * @return 75 | * @throws Exception 76 | */ 77 | @RequestMapping(value = "personal_listAccounts", method = RequestMethod.POST) 78 | public Object personal_listAccounts() throws Exception { 79 | return rpcService.personal_listAccounts(); 80 | } 81 | 82 | /** 83 | * create new Account 84 | * 85 | * @return 86 | * @throws Exception 87 | */ 88 | @RequestMapping(value = "personal_newAccount", method = RequestMethod.POST) 89 | public Object personal_newAccount(@RequestBody NewAccountDTO newAccountDTO) throws Exception { 90 | return rpcService.personal_newAccount(newAccountDTO.getPassphrase()); 91 | } 92 | 93 | /** 94 | * import key 95 | * 96 | * @return 97 | * @throws Exception 98 | */ 99 | @RequestMapping(value = "personal_importRawKey", method = RequestMethod.POST) 100 | public Object personal_importRawKey(@RequestBody ImportRawKeyDTO importRawKeyDTO) throws Exception { 101 | return rpcService.personal_importRawKey(importRawKeyDTO.getKeydata(), importRawKeyDTO.getPassphrase()); 102 | } 103 | 104 | /** 105 | * get publickey for RSA 106 | * 107 | * @return 108 | * @throws Exception 109 | */ 110 | @RequestMapping(value = "publicKey", method = RequestMethod.POST) 111 | public Object publicKey() throws Exception { 112 | return RSACoder.getPublicKey(); 113 | } 114 | 115 | /** 116 | * get TransactionCount for nonce 117 | * 118 | * @return 119 | * @throws Exception 120 | */ 121 | @RequestMapping(value = "transactionCount", method = RequestMethod.POST) 122 | public Object getTransactionCount(@RequestBody TransactionCountDTO transactionCountDTO) throws Exception { 123 | return rpcService.getTransactionCount(transactionCountDTO.getAddress()); 124 | } 125 | 126 | /** 127 | * personal by keyDate 128 | * @param file 129 | * @param passhphrase 130 | * @return 131 | * @throws Exception 132 | */ 133 | @RequestMapping(value = "personalByKeyDate", method = RequestMethod.POST) 134 | public Object eth_personalByKeyDate(@RequestParam("file") MultipartFile file, @RequestParam String passhphrase) throws Exception { 135 | String source = FileUtil.readFile(file.getInputStream()); 136 | return rpcService.eth_personalByKeyDate(source, passhphrase); 137 | } 138 | 139 | /** 140 | * personal by privateKey 141 | * @param personalByPrivateKeyDTO 142 | * @return 143 | * @throws Exception 144 | */ 145 | @RequestMapping(value = "personalByPrivateKey", method = RequestMethod.POST) 146 | public Object eth_personalByPrivateKey(@RequestBody PersonalByPrivateKeyDTO personalByPrivateKeyDTO) throws Exception { 147 | return rpcService.eth_personalByPrivateKey(personalByPrivateKeyDTO.getPrivateKey()); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/utils/RSACoder.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.utils; 2 | 3 | import org.apache.commons.codec.binary.Base64; 4 | 5 | import javax.crypto.Cipher; 6 | import java.security.*; 7 | import java.security.spec.PKCS8EncodedKeySpec; 8 | import java.security.spec.X509EncodedKeySpec; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * Created by lake on 17-4-12. 14 | */ 15 | public class RSACoder { 16 | public static final String KEY_ALGORITHM = "RSA"; 17 | public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; 18 | 19 | private static final String PUBLIC_KEY = "MvcRSAPublicKey"; 20 | private static final String PRIVATE_KEY = "MvcRSAPrivateKey"; 21 | 22 | private static Map keyMap = null; 23 | 24 | public static byte[] decryptBASE64(String key) { 25 | return Base64.decodeBase64(key); 26 | } 27 | 28 | public static String encryptBASE64(byte[] bytes) { 29 | return Base64.encodeBase64String(bytes); 30 | } 31 | 32 | static { 33 | try { 34 | keyMap = initKey(); 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | /** 41 | * 用私钥对信息生成数字签名 42 | * 43 | * @param data 加密数据 44 | * @param privateKey 私钥 45 | * @return 46 | * @throws Exception 47 | */ 48 | public static String sign(byte[] data, String privateKey) throws Exception { 49 | // 解密由base64编码的私钥 50 | byte[] keyBytes = decryptBASE64(privateKey); 51 | // 构造PKCS8EncodedKeySpec对象 52 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); 53 | // KEY_ALGORITHM 指定的加密算法 54 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 55 | // 取私钥匙对象 56 | PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); 57 | // 用私钥对信息生成数字签名 58 | Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); 59 | signature.initSign(priKey); 60 | signature.update(data); 61 | return encryptBASE64(signature.sign()); 62 | } 63 | 64 | /** 65 | * 校验数字签名 66 | * 67 | * @param data 加密数据 68 | * @param publicKey 公钥 69 | * @param sign 数字签名 70 | * @return 校验成功返回true 失败返回false 71 | * @throws Exception 72 | */ 73 | public static boolean verify(byte[] data, String publicKey, String sign) 74 | throws Exception { 75 | // 解密由base64编码的公钥 76 | byte[] keyBytes = decryptBASE64(publicKey); 77 | // 构造X509EncodedKeySpec对象 78 | X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 79 | // KEY_ALGORITHM 指定的加密算法 80 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 81 | // 取公钥匙对象 82 | PublicKey pubKey = keyFactory.generatePublic(keySpec); 83 | Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); 84 | signature.initVerify(pubKey); 85 | signature.update(data); 86 | // 验证签名是否正常 87 | return signature.verify(decryptBASE64(sign)); 88 | } 89 | 90 | public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception{ 91 | // 对密钥解密 92 | byte[] keyBytes = decryptBASE64(key); 93 | // 取得私钥 94 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); 95 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 96 | Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); 97 | // 对数据解密 98 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 99 | cipher.init(Cipher.DECRYPT_MODE, privateKey); 100 | return cipher.doFinal(data); 101 | } 102 | 103 | /** 104 | * 解密
105 | * 用私钥解密 106 | * 107 | * @param data 108 | * @param key 109 | * @return 110 | * @throws Exception 111 | */ 112 | public static byte[] decryptByPrivateKey(String data, String key){ 113 | try { 114 | return decryptByPrivateKey(decryptBASE64(data),key); 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | return null; 119 | } 120 | 121 | /** 122 | * 解密
123 | * 用公钥解密 124 | * 125 | * @param data 126 | * @param key 127 | * @return 128 | * @throws Exception 129 | */ 130 | public static byte[] decryptByPublicKey(byte[] data, String key) 131 | throws Exception { 132 | // 对密钥解密 133 | byte[] keyBytes = decryptBASE64(key); 134 | // 取得公钥 135 | X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); 136 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 137 | Key publicKey = keyFactory.generatePublic(x509KeySpec); 138 | // 对数据解密 139 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 140 | cipher.init(Cipher.DECRYPT_MODE, publicKey); 141 | return cipher.doFinal(data); 142 | } 143 | 144 | /** 145 | * 加密
146 | * 用公钥加密 147 | * 148 | * @param data 149 | * @param key 150 | * @return 151 | * @throws Exception 152 | */ 153 | public static byte[] encryptByPublicKey(String data, String key) 154 | throws Exception { 155 | // 对公钥解密 156 | byte[] keyBytes = decryptBASE64(key); 157 | // 取得公钥 158 | X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); 159 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 160 | Key publicKey = keyFactory.generatePublic(x509KeySpec); 161 | // 对数据加密 162 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 163 | cipher.init(Cipher.ENCRYPT_MODE, publicKey); 164 | return cipher.doFinal(data.getBytes()); 165 | } 166 | 167 | /** 168 | * 加密
169 | * 用私钥加密 170 | * 171 | * @param data 172 | * @param key 173 | * @return 174 | * @throws Exception 175 | */ 176 | public static byte[] encryptByPrivateKey(byte[] data, String key) 177 | throws Exception { 178 | // 对密钥解密 179 | byte[] keyBytes = decryptBASE64(key); 180 | // 取得私钥 181 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); 182 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 183 | Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); 184 | // 对数据加密 185 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 186 | cipher.init(Cipher.ENCRYPT_MODE, privateKey); 187 | return cipher.doFinal(data); 188 | } 189 | 190 | /** 191 | * 取得私钥 192 | * 193 | * @param keyMap 194 | * @return 195 | * @throws Exception 196 | */ 197 | public static String getPrivateKey(Map keyMap) 198 | throws Exception { 199 | Key key = (Key) keyMap.get(PRIVATE_KEY); 200 | return encryptBASE64(key.getEncoded()); 201 | } 202 | 203 | /** 204 | * 取得私钥 205 | * 206 | * @param keyMap 207 | * @return 208 | * @throws Exception 209 | */ 210 | public static String getPrivateKey() 211 | throws Exception { 212 | Key key = (Key) keyMap.get(PRIVATE_KEY); 213 | return encryptBASE64(key.getEncoded()); 214 | } 215 | 216 | /** 217 | * 取得公钥 218 | * 219 | * @param keyMap 220 | * @return 221 | * @throws Exception 222 | */ 223 | public static String getPublicKey(Map keyMap) 224 | throws Exception { 225 | Key key = keyMap.get(PUBLIC_KEY); 226 | return encryptBASE64(key.getEncoded()); 227 | } 228 | 229 | /** 230 | * 取得公钥 231 | * 232 | * @param keyMap 233 | * @return 234 | * @throws Exception 235 | */ 236 | public static String getPublicKey() 237 | throws Exception { 238 | Key key = keyMap.get(PUBLIC_KEY); 239 | return encryptBASE64(key.getEncoded()); 240 | } 241 | 242 | /** 243 | * 初始化密钥 244 | * 245 | * @return 246 | * @throws Exception 247 | */ 248 | public static Map initKey() throws Exception { 249 | KeyPairGenerator keyPairGen = KeyPairGenerator 250 | .getInstance(KEY_ALGORITHM); 251 | keyPairGen.initialize(1024); 252 | KeyPair keyPair = keyPairGen.generateKeyPair(); 253 | Map keyMap = new HashMap(2); 254 | keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥 255 | keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥 256 | return keyMap; 257 | } 258 | 259 | } -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/service/RpcServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.service; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.mvc.polymorphic.model.JsonCredentials; 5 | import com.mvc.polymorphic.model.TransactionResponse; 6 | import com.mvc.polymorphic.utils.RSACoder; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.http.HttpMethod; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.util.Assert; 13 | import org.springframework.web.client.RestTemplate; 14 | import org.web3j.abi.FunctionEncoder; 15 | import org.web3j.abi.TypeReference; 16 | import org.web3j.abi.datatypes.Address; 17 | import org.web3j.abi.datatypes.Function; 18 | import org.web3j.abi.datatypes.Type; 19 | import org.web3j.abi.datatypes.generated.Uint256; 20 | import org.web3j.crypto.Credentials; 21 | import org.web3j.crypto.ECKeyPair; 22 | import org.web3j.crypto.Wallet; 23 | import org.web3j.crypto.WalletFile; 24 | import org.web3j.protocol.Web3j; 25 | import org.web3j.protocol.admin.Admin; 26 | import org.web3j.protocol.admin.methods.response.NewAccountIdentifier; 27 | import org.web3j.protocol.admin.methods.response.PersonalListAccounts; 28 | import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount; 29 | import org.web3j.protocol.core.DefaultBlockParameter; 30 | import org.web3j.protocol.core.DefaultBlockParameterName; 31 | import org.web3j.protocol.core.methods.request.Transaction; 32 | import org.web3j.protocol.core.methods.response.*; 33 | import org.web3j.protocol.geth.Geth; 34 | import org.web3j.quorum.Quorum; 35 | import org.web3j.quorum.methods.request.PrivateTransaction; 36 | import org.web3j.utils.Numeric; 37 | 38 | import java.io.IOException; 39 | import java.math.BigDecimal; 40 | import java.math.BigInteger; 41 | import java.util.Arrays; 42 | import java.util.Collections; 43 | import java.util.List; 44 | import java.util.concurrent.ExecutionException; 45 | 46 | import static org.web3j.tx.Contract.GAS_LIMIT; 47 | import static org.web3j.utils.Convert.Unit; 48 | import static org.web3j.utils.Convert.fromWei; 49 | 50 | @Component 51 | public class RpcServiceImpl implements RpcService { 52 | @Autowired 53 | private Admin admin; 54 | @Autowired 55 | private Geth geth; 56 | @Autowired 57 | private Web3j web3j; 58 | @Autowired 59 | private Quorum quorum; 60 | @Value("${trans.log.url}") 61 | String transLogUrl; 62 | @Autowired 63 | RestTemplate restTemplate; 64 | 65 | @Override 66 | public Object eth_personalByKeyDate (String source, String passhphrase) throws Exception { 67 | passhphrase = new String(RSACoder.decryptByPrivateKey(passhphrase, RSACoder.getPrivateKey())); 68 | ObjectMapper objectMapper = new ObjectMapper(); 69 | WalletFile file = objectMapper.readValue(source, WalletFile.class); 70 | ECKeyPair ecKeyPair = Wallet.decrypt(passhphrase, file); 71 | Credentials credentials = Credentials.create(ecKeyPair); 72 | return new JsonCredentials(credentials); 73 | } 74 | 75 | @Override 76 | public Object eth_personalByPrivateKey (String privateKey) throws Exception { 77 | privateKey = new String(RSACoder.decryptByPrivateKey(privateKey, RSACoder.getPrivateKey())); 78 | return new JsonCredentials(Credentials.create(privateKey)); 79 | } 80 | 81 | @Override 82 | public Object eth_getBalance(String address, String blockId) throws Exception { 83 | // admin.ethGetStorageAt(address, BigInteger.ZERO, DefaultBlockParameterName.LATEST).send() 84 | EthGetBalance response = web3j.ethGetBalance(address, DefaultBlockParameter.valueOf(blockId)).send(); 85 | BigDecimal result = fromWei(String.valueOf(response.getBalance()), Unit.ETHER); 86 | return result; 87 | } 88 | 89 | @Override 90 | public Object eth_getTransactionByHash(String transactionHash) throws Exception { 91 | EthTransaction response = web3j.ethGetTransactionByHash(transactionHash).send(); 92 | return response; 93 | } 94 | 95 | @Override 96 | public Object eth_sendTransaction(Transaction transaction, String pass) throws Exception { 97 | pass = new String(RSACoder.decryptByPrivateKey(pass, RSACoder.getPrivateKey())); 98 | PersonalUnlockAccount flag = admin.personalUnlockAccount(transaction.getFrom(), pass).send(); 99 | Assert.isTrue(flag.accountUnlocked(), "unlock error"); 100 | EthSendTransaction response = admin.ethSendTransaction(transaction).send(); 101 | return response; 102 | } 103 | 104 | private TransactionResponse processEventResponse( 105 | List eventResponses, TransactionReceipt transactionReceipt, java.util.function.Function map) { 106 | if (!eventResponses.isEmpty()) { 107 | return new TransactionResponse<>( 108 | transactionReceipt.getTransactionHash(), 109 | map.apply(eventResponses.get(0))); 110 | } else { 111 | return new TransactionResponse<>( 112 | transactionReceipt.getTransactionHash()); 113 | } 114 | } 115 | 116 | @Override 117 | public Object eth_sendTransaction(Transaction transaction, String pass, String contractAddress) throws Exception { 118 | pass = new String(RSACoder.decryptByPrivateKey(pass, RSACoder.getPrivateKey())); 119 | PersonalUnlockAccount flag = admin.personalUnlockAccount(transaction.getFrom(), pass).send(); 120 | Assert.isTrue(flag.accountUnlocked(), "unlock error"); 121 | Function function = new Function("transfer", Arrays.asList(new Address(transaction.getTo()), new Uint256(Numeric.decodeQuantity(transaction.getValue()))), Collections.>emptyList()); 122 | String data = FunctionEncoder.encode(function); 123 | PrivateTransaction privateTransaction = new PrivateTransaction(transaction.getFrom(), null,GAS_LIMIT, contractAddress, BigInteger.ZERO, data,Arrays.asList(transaction.getFrom(), transaction.getTo(), "0xc83783e5f32d1157498e6374b6ab2aec48ff4428") ); 124 | EthSendTransaction response = quorum.ethSendTransaction(privateTransaction).send(); 125 | 126 | return response; 127 | } 128 | 129 | @Override 130 | public Object ethSendRawTransaction(String signedMessage) throws Exception { 131 | EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(signedMessage).send(); 132 | return ethSendTransaction; 133 | } 134 | 135 | @Override 136 | public Object personal_newAccount(String passhphrase) throws Exception { 137 | passhphrase = new String(RSACoder.decryptByPrivateKey(passhphrase, RSACoder.getPrivateKey())); 138 | NewAccountIdentifier response = admin.personalNewAccount(passhphrase).send(); 139 | geth.personalUnlockAccount(response.getAccountId(), passhphrase).send(); 140 | return response; 141 | } 142 | 143 | @Override 144 | public Object personal_listAccounts() throws IOException { 145 | PersonalListAccounts response = geth.personalListAccounts().send(); 146 | return response; 147 | } 148 | 149 | @Override 150 | public Object personal_importRawKey(String keydata, String passphrase) throws Exception { 151 | passphrase = new String(RSACoder.decryptByPrivateKey(passphrase, RSACoder.getPrivateKey())); 152 | keydata = new String(RSACoder.decryptByPrivateKey(keydata, RSACoder.getPrivateKey())); 153 | return geth.personalImportRawKey(keydata, passphrase).send(); 154 | } 155 | 156 | @Override 157 | public Object parityExportAccount(String address, String passphrase) throws IOException { 158 | return null; 159 | } 160 | 161 | @Override 162 | public Object getTransactionCount(String address) throws ExecutionException, InterruptedException { 163 | EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.LATEST).sendAsync().get(); 164 | BigInteger nonce = ethGetTransactionCount.getTransactionCount(); 165 | return nonce; 166 | } 167 | 168 | @Override 169 | public Object txList(String address) { 170 | 171 | 172 | String url = transLogUrl + String.format( EtherscanUrl.txlist, address); 173 | ResponseEntity response = new RestTemplate().exchange(url, HttpMethod.GET, null, String.class); 174 | System.out.println(response.getHeaders()); 175 | return response.getBody(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/mvc/polymorphic/service/BchService.java: -------------------------------------------------------------------------------- 1 | package com.mvc.polymorphic.service; 2 | 3 | import com.mvc.bitcoincashj.core.*; 4 | import com.mvc.bitcoincashj.core.listeners.TransactionConfidenceEventListener; 5 | import com.mvc.bitcoincashj.kits.WalletAppKit; 6 | import com.mvc.bitcoincashj.params.MainNetParams; 7 | import com.mvc.bitcoincashj.params.TestNet3Params; 8 | import com.mvc.bitcoincashj.script.Script; 9 | import com.mvc.bitcoincashj.wallet.Wallet; 10 | import com.mvc.bitcoincashj.wallet.listeners.KeyChainEventListener; 11 | import com.mvc.bitcoincashj.wallet.listeners.ScriptsChangeEventListener; 12 | import com.mvc.bitcoincashj.wallet.listeners.WalletCoinsReceivedEventListener; 13 | import com.mvc.bitcoincashj.wallet.listeners.WalletCoinsSentEventListener; 14 | import com.mvc.polymorphic.common.BlockChainService; 15 | import com.mvc.polymorphic.common.BlockResult; 16 | import com.mvc.polymorphic.common.bean.BchTransaction; 17 | import lombok.extern.java.Log; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.util.StringUtils; 20 | 21 | import java.io.File; 22 | import java.math.BigDecimal; 23 | import java.util.Arrays; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * bcc service 30 | * 31 | * @author qiyichen 32 | * @create 2018/4/12 13:43 33 | */ 34 | @Service("BchService") 35 | @Log 36 | public class BchService extends BlockChainService { 37 | 38 | private WalletAppKit kit; 39 | private final static String DEFAULT_FILE_PREFIX = "DEFAULT_FILE_PREFIX"; 40 | private final static String TEST_KEY = "local"; 41 | private final static String TOKEN_NAME = "bch"; 42 | private static Boolean initFinished = false; 43 | 44 | @Override 45 | protected BlockResult getBalance(String address) { 46 | Object result = kit.wallet().getBalance().toFriendlyString(); 47 | return tokenSuccess(TOKEN_NAME, result); 48 | } 49 | 50 | @Override 51 | protected BlockResult getTransactionByHash(String transactionHash) { 52 | Transaction trans = kit.wallet().getTransaction(Sha256Hash.wrap(transactionHash)); 53 | BchTransaction transaction = BchTransaction.build(trans, kit.wallet()); 54 | return tokenSuccess(TOKEN_NAME, transaction); 55 | } 56 | 57 | @Override 58 | protected BlockResult sendTransaction(String pass, String from, String toAddress, BigDecimal data) { 59 | String blockEnv = tokenConfig.getEnv().get(TOKEN_NAME); 60 | if (!initFinished) { 61 | return tokenFail(TOKEN_NAME, String.format("wallet is async, please wait, now height is %s", kit.wallet().getLastBlockSeenHeight())); 62 | } 63 | log.info("send money to: " + toAddress); 64 | Coin value = Coin.parseCoin(String.valueOf(data)); 65 | // if the wallet have more than 1 ecKey, we need to choose one for pay 66 | Address to = Address.fromBase58(kit.params(), toAddress); 67 | Wallet.SendResult result = null; 68 | try { 69 | if (TEST_KEY.equalsIgnoreCase(blockEnv)) { 70 | kit.peerGroup().setMaxConnections(4); 71 | } 72 | result = kit.wallet().sendCoins(kit.peerGroup(), to, value, true); 73 | } catch (Exception e) { 74 | return tokenFail(TOKEN_NAME, e.getMessage()); 75 | } finally { 76 | if (!kit.wallet().isEncrypted()) { 77 | kit.wallet().encrypt(pass); 78 | } 79 | } 80 | log.info("coins sent. transaction hash: " + result.tx.getHashAsString()); 81 | return tokenSuccess(TOKEN_NAME, result.tx.getHashAsString()); 82 | } 83 | 84 | @Override 85 | protected BlockResult newAccount(String pass) { 86 | if (!initFinished) { 87 | return tokenFail(TOKEN_NAME, String.format("wallet is async, please wait, now height is %s", kit.wallet().getLastBlockSeenHeight())); 88 | } 89 | Address address = kit.wallet().freshReceiveAddress(); 90 | kit.wallet().addWatchedAddress(address); 91 | return tokenSuccess(TOKEN_NAME, address.toString()); 92 | } 93 | 94 | @Override 95 | protected BlockResult getConfirmation(String transactionHash) { 96 | BchTransaction transaction = (BchTransaction) getTransactionByHash(transactionHash).getResult(); 97 | return tokenSuccess(TOKEN_NAME, transaction.getDepth()); 98 | } 99 | 100 | @Override 101 | protected void onTransaction(Object... objects) { 102 | System.out.println("db save"); 103 | } 104 | 105 | @Override 106 | public void run(String... args) throws Exception { 107 | 108 | if (!StringUtils.isEmpty(tokenConfig.getEnv().get(TOKEN_NAME))) { 109 | String blockEnv = tokenConfig.getEnv().get(TOKEN_NAME); 110 | String blockPath = tokenConfig.getPath().get(TOKEN_NAME).get(blockEnv); 111 | WalletAppKit kit = new WalletAppKit(getNetWork(blockEnv), new File(blockPath + blockEnv), DEFAULT_FILE_PREFIX); 112 | this.kit = kit; 113 | System.out.println("BCH Service initialized and nothing happened."); 114 | startListen(); 115 | // init wallet 116 | if (kit.wallet().getImportedKeys().size() == 0) { 117 | String pass = tokenConfig.getPass().get(TOKEN_NAME).get(blockEnv); 118 | ECKey ecKey = new ECKey(); 119 | kit.wallet().encrypt(pass); 120 | kit.wallet().importKeysAndEncrypt(Arrays.asList(ecKey), pass); 121 | kit.wallet().addWatchedAddress(ecKey.toAddress(kit.params())); 122 | } 123 | initFinished = true; 124 | } else { 125 | log.info("BCH not supported!"); 126 | } 127 | } 128 | 129 | private void startListen() { 130 | log.info("Start peer group"); 131 | kit.startAsync(); 132 | kit.awaitRunning(); 133 | log.info("Downloading block chain"); 134 | log.info("start listen"); 135 | kit.wallet().addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() { 136 | @Override 137 | public void onCoinsReceived(Wallet wallet, Transaction transaction, Coin coin, Coin coin1) { 138 | System.out.println("coins received"); 139 | Map map = new HashMap<>(); 140 | onTransaction(map); 141 | } 142 | }); 143 | 144 | kit.wallet().addCoinsSentEventListener(new WalletCoinsSentEventListener() { 145 | @Override 146 | public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { 147 | System.out.println("coins sent"); 148 | Map map = new HashMap<>(); 149 | onTransaction(map); 150 | } 151 | }); 152 | 153 | kit.wallet().addKeyChainEventListener(new KeyChainEventListener() { 154 | @Override 155 | public void onKeysAdded(List keys) { 156 | System.out.println("new key added"); 157 | Map map = new HashMap<>(); 158 | onTransaction(map); 159 | } 160 | }); 161 | 162 | kit.wallet().addScriptsChangeEventListener(new ScriptsChangeEventListener() { 163 | @Override 164 | public void onScriptsChanged(Wallet wallet, List