0


Spring Boot项目中集成Geth与以太坊区块链进行交互操作实例

前置条件已经安装Geth并启动。
现在我们讲一下Spring Boot项目中集成Geth,然后怎么以太坊区块链进行交互操作。

1、添加依赖到工程pom.xml

<dependency><groupId>org.web3j</groupId><artifactId>core</artifactId><version>4.8.7</version></dependency><dependency><groupId>org.web3j</groupId><artifactId>geth</artifactId><version>4.8.7</version></dependency>

2、添加配置到yml文件

web3j:#  client-address: http://192.168.99.100:8545client-address: http://127.0.0.1:8545admin-client:truehttpTimeoutSeconds:60000

3、ETH配置类EthConfig.java

/**
 * @author deray.wang
 * @date 2024/04/20 17:18
 */@ConfigurationpublicclassEthConfig{@Value("${web3j.client-address}")privateString rpc;@BeanpublicWeb3jweb3j(){OkHttpClient.Builder builder =newOkHttpClient.Builder();
        builder.connectTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.writeTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.readTimeout(30*1000,TimeUnit.MILLISECONDS);OkHttpClient httpClient = builder.build();Web3j web3j =Web3j.build(newHttpService(rpc,httpClient,false));return web3j;}/**
     * 初始化admin级别操作的对象
     * @return Admin
     */@BeanpublicAdminadmin(){OkHttpClient.Builder builder =newOkHttpClient.Builder();
        builder.connectTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.writeTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.readTimeout(30*1000,TimeUnit.MILLISECONDS);OkHttpClient httpClient = builder.build();Admin admin =Admin.build(newHttpService(rpc,httpClient,false));return admin;}/**
     * 初始化personal级别操作的对象
     * @return Geth
     */@BeanpublicGethgeth(){OkHttpClient.Builder builder =newOkHttpClient.Builder();
        builder.connectTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.writeTimeout(30*1000,TimeUnit.MILLISECONDS);
        builder.readTimeout(30*1000,TimeUnit.MILLISECONDS);OkHttpClient httpClient = builder.build();Geth geth =Geth.build(newHttpService(rpc,httpClient,false));return geth;}}

4、封装两个bean:转账ETH参数类 TransferEchBean.java 和BlockchainTransaction.java

/**
 * 转账ETH参数类
 * @author
 */@Data@ApiModel@ToStringpublicclassTransferEchBean{@ApiModelProperty("fromAddr")privateString fromAddr;@ApiModelProperty("密码")privateString privateKey;@ApiModelProperty("toAddr")privateString toAddr;@ApiModelProperty("amount")privateBigDecimal amount;@ApiModelProperty("data")privateString data;}
/**
 * @author deray.wang
 * @date 2024/04/20 13:44
 */@DatapublicclassBlockchainTransaction{privateString id;//发送发件人IDprivateInteger fromId;//交易金额privatelong value;//收件人IDprivateInteger toId;privateBoolean accepted;}

5、封装操作区块链的方法 BlockchainService.java

BlockchainService.java类

/**
 * @author deray.wang
 * @date 2024/04/20 13:36
 */publicinterfaceBlockchainService{/**
     * 获取账户的Nonce
     * @param web3j
     * @param addr
     * @return
     */BigIntegergetAcountNonce(Web3j web3j,String addr);/**
     * 获取账户余额
     * @param web3j
     * @param addr
     * @return
     */BigDecimalgetAccountBalance(Web3j web3j,String addr);/**
     * 查询区块内容
     * @param web3j
     * @param blockNumber
     * @return
     */EthBlockgetBlockEthBlock(Web3j web3j,BigInteger blockNumber);/**
     * 创建钱包
     * @param password
     * @return
     */ServiceResponsenewAccount(String password);/**
     * 地址列表
     * @return
     */List<String>getAllAccounts();/**
     * 转账ETH
     * @param web3j
     * @param fromAddr
     * @param privateKey
     * @param toAddr
     * @param amount
     * @param data
     * @return
     */ServiceResponsetransferETHD(Web3j web3j,TransferEchBean filterBean);/**
     * 普通转账ETH
     * @param web3j
     * @param filterBean
     * @return
     */ServiceResponsetranETH(Web3j web3j,TransferEchBean filterBean);

实现类:BlockchainServiceImpl.java

/**
 * @author deray.wang
 * @date 2024/11/20 13:52
 */@ServicepublicclassBlockchainServiceImplimplementsBlockchainService{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(BlockchainService.class);privatestaticBigInteger gNoce =null;@Value("${wallet.file}")privateStringFILE;@AutowiredprivateAdmin admin;@AutowiredprivatestaticWeb3j web3j;@AutowiredprivateGeth geth;/**
     * 获取账户的Nonce
     * @param web3j
     * @param addr
     * @return
     */@OverridepublicBigIntegergetAcountNonce(Web3j web3j,String addr){returngetNonce(web3j,addr);}@OverridepublicBigDecimalgetAccountBalance(Web3j web3j,String addr){returngetBalance(web3j,addr);}/**
     *
     */@OverridepublicServiceResponsetransferETHD(Web3j web3j,TransferEchBean filterBean){//封装业务参数Map<String,String> map =newHashMap<String,String>();
        map.put("time",String.valueOf(newDate()));
        map.put("type","info");
        map.put("msg","Web3 Test!!!000000000000000000000000000");JSONObject jsonObj=newJSONObject(map);//将data转化为hexString datahex =null;try{
            datahex =HexUtils.toHexString(jsonObj.toString().getBytes("UTF-8"));}catch(UnsupportedEncodingException e){
            e.printStackTrace();}returntransferETH(web3j,filterBean.getFromAddr(),filterBean.getPrivateKey(),filterBean.getToAddr(),filterBean.getAmount(),datahex);}/**
     *
     */@OverridepublicServiceResponsetranETH(Web3j web3j,TransferEchBean filterBean){
        。。
        returnServiceResponse.createFailResponse("",0,"");}/**
     * 指定地址发送交易所需nonce获取
     * @param web3j
     * @param addr
     * @return
     */publicstaticBigIntegergetNonce(Web3j web3j,String addr){EthGetTransactionCount transactionCount =null;try{
            transactionCount = web3j.ethGetTransactionCount(addr,DefaultBlockParameterName.LATEST).sendAsync().get();}catch(InterruptedException e){
            e.printStackTrace();}catch(ExecutionException e){
            e.printStackTrace();}BigInteger nonce = transactionCount.getTransactionCount();//        LOGGER.info("Tx hash: {}",  "transfer nonce : " + nonce);return nonce;}/**
     * 获取代币余额
     * @param web3j
     * @param fromAddress
     * @param contractAddress
     * @return
     */publicstaticBigIntegergetTokenBalance(Web3j web3j,String fromAddress,String contractAddress){String methodName ="balanceOf";List<Type> inputParameters =newArrayList<>();List<TypeReference<?>> outputParameters =newArrayList<>();Address address =newAddress(fromAddress);
        inputParameters.add(address);TypeReference<Uint256> typeReference =newTypeReference<Uint256>(){};
        outputParameters.add(typeReference);Function function =newFunction(methodName, inputParameters, outputParameters);String data =FunctionEncoder.encode(function);Transaction transaction =Transaction.createEthCallTransaction(fromAddress, contractAddress, data);EthCall ethCall;BigInteger balanceValue =BigInteger.ZERO;try{
            ethCall = web3j.ethCall(transaction,DefaultBlockParameterName.LATEST).send();List<Type> results =FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
            balanceValue =(BigInteger) results.get(0).getValue();}catch(IOException e){
            e.printStackTrace();}return balanceValue;}/**
     * 转账ETH
     * @param web3j
     * @param fromAddr 发起人钱包地址
     * @param privateKey 钱包私钥
     * @param toAddr 转入的钱包地址
     * @param amount 转账金额,单位是wei
     * @param data  备注的信息
     * @param
     * @return
     */ServiceResponsetransferETH(Web3j web3j,String fromAddr,String privateKey,String toAddr,BigDecimal amount,String data){// 获得nonceBigInteger nonce =getNonce(web3j, fromAddr);// value转换BigInteger value =Convert.toWei(amount,Convert.Unit.ETHER).toBigInteger();// gasPrice转账费用BigInteger gasPrice;

        gasPrice =Convert.toWei("0",Convert.Unit.GWEI).toBigInteger();//注意手续费的设置,这块很容易遇到问题BigInteger gasLimit =Convert.toWei("45000",Convert.Unit.WEI).toBigInteger();// 查询调用者余额,检测余额是否充足BigDecimal ethBalance =getBalance(web3j, fromAddr);BigDecimal balance =Convert.toWei(ethBalance,Convert.Unit.ETHER);BigDecimal tt =Convert.toWei(String.valueOf(1),Convert.Unit.ETHER);checkMoney(String.valueOf(amount),String.valueOf(balance));BigInteger val = gasPrice.multiply(gasLimit);if(balance.compareTo(tt.add(newBigDecimal(val)))<0){//throw new RuntimeException("余额不足,请核实");returnServiceResponse.createFailResponse("",0,"交易失败:余额不足,请核实");}//对交易签名,并发送交易if(gNoce ==null){
             gNoce = nonce;}LOGGER.info("Tx hash: {}","transfer nonce : "+ gNoce+" gasPrice:"+gasPrice+" gasLimit:"+gasLimit+" toAddr:"+toAddr+" value:"+value+" data:"+data);RawTransaction rawTransaction =RawTransaction.createTransaction(gNoce, gasPrice, gasLimit, toAddr, value, data);
         gNoce = gNoce.add(newBigInteger("1"));//RawTransaction.createEtherTransaction(nonce,gasPrice,gasLimit,to,value);if(privateKey.startsWith("0x")){
             privateKey = privateKey.substring(2);}ECKeyPair ecKeyPair =ECKeyPair.create(newBigInteger(privateKey,16));Credentials credentials =Credentials.create(ecKeyPair);System.out.println(credentials.getAddress());System.out.println("PrivateKey:"+ credentials.getEcKeyPair().getPrivateKey());//进行签名操作 签名Transaction,这里要对交易做签名byte[] signedMessage =TransactionEncoder.signMessage(rawTransaction, credentials);String hexValue =Numeric.toHexString(signedMessage);//发送交易EthSendTransaction ethSendTransaction =null;if(!"".equals(hexValue)){try{
                 ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();if(ethSendTransaction.hasError()){String message = ethSendTransaction.getError().getMessage();System.out.println("transaction failed,info:"+ message);//Utils.writeFile("F:/testErr.txt", "transaction failed,info:" + message);returnServiceResponse.createFailResponse("",0,"交易失败:"+message);}}catch(InterruptedException e){System.out.println("transaction failed,info:"+ ethSendTransaction.getError().getMessage());
                 e.printStackTrace();returnServiceResponse.createFailResponse("",0,"交易失败:"+ethSendTransaction.getError().getMessage());}catch(ExecutionException e){System.out.println("transaction failed,info:"+ ethSendTransaction.getError().getMessage());
                 e.printStackTrace();returnServiceResponse.createFailResponse("",0,"交易失败:"+ethSendTransaction.getError().getMessage());}}String transactionHash = ethSendTransaction.getTransactionHash();if(ethSendTransaction.hasError()){String message=ethSendTransaction.getError().getMessage();returnServiceResponse.createFailResponse("",0,"交易失败:"+message);}else{EthGetTransactionReceipt send =null;try{
                 send = web3j.ethGetTransactionReceipt(transactionHash).send();}catch(IOException e){
                 e.printStackTrace();}if(send !=null){System.out.println("交易成功");//System.out.println(send.getTransactionReceipt());}Map<String,String> mapRes =newHashMap<String,String>();
             mapRes.put("txHash", transactionHash);returnServiceResponse.createSuccessResponse("",mapRes,"交易成功,等待记账!");}}/**
     * 获取ETH余额
     * @param web3j
     * @param address
     * @return
     */publicstaticBigDecimalgetBalance(Web3j web3j,String address){try{EthGetBalance ethGetBalance = web3j.ethGetBalance(address,DefaultBlockParameterName.LATEST).send();//单位转换BigDecimal banlance =Convert.fromWei(newBigDecimal(ethGetBalance.getBalance()),Convert.Unit.ETHER);return banlance;}catch(IOException e){
            e.printStackTrace();//throw new Exception("查询钱包余额失败");returnnull;}}publicstaticBigIntegergetTransactionGasLimit(Web3j web3j,Transaction transaction){try{EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();if(ethEstimateGas.hasError()){thrownewRuntimeException(ethEstimateGas.getError().getMessage());}return ethEstimateGas.getAmountUsed();}catch(IOException e){thrownewRuntimeException("net error");}}/**
     * generate a random group of mnemonics
     * 生成一组随机的助记词
     */privateStringgenerateMnemonics(){byte[] initialEntropy =newbyte[16];newSecureRandom().nextBytes(initialEntropy);String mnemonic =MnemonicUtils.generateMnemonic(initialEntropy);return mnemonic;}privateMap<String,String>createAccount(){//创建Map对象Map<String,String> map =newHashMap<String,String>();//数据采用的哈希表结构Bip39Wallet wallet =null;// 创建一个存放keystore的文件夹String path =FILE;try{// 创建钱包
            wallet =WalletUtils.generateBip39Wallet("",newFile(path));}catch(Exception e){LOGGER.info("创建钱包失败");}// 获取keystore的名字String keyStoreKey = wallet.getFilename();LOGGER.info("keyStoreKey ================ "+ keyStoreKey);// 获取助记词String mnemonic = wallet.getMnemonic();LOGGER.info("mnemonic ======================== "+ mnemonic);// 使用密码和助记词让账户解锁Credentials credentials =WalletUtils.loadBip39Credentials("", wallet.getMnemonic());// 获取账户地址String address = credentials.getAddress();LOGGER.info("address ================= "+ address);// 获取公钥String publicKey = credentials.getEcKeyPair().getPublicKey().toString(16);LOGGER.info("publicKey ==================== "+ publicKey);// 获取私钥String privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16);LOGGER.info("privateKey ================== "+ privateKey);
        map.put("address",address);
        map.put("privateKey",privateKey);
        map.put("publicKey",publicKey);
        map.put("createTime",TimeUtil.getNowString());return map;}/**
     * 查询区块内容
     * @param web3j
     * @param blockNumber
     * @return
     */@OverridepublicEthBlockgetBlockEthBlock(Web3j web3j,BigInteger blockNumber){DefaultBlockParameter defaultBlockParameter =newDefaultBlockParameterNumber(blockNumber);Request<?,EthBlock> request = web3j.ethGetBlockByNumber(defaultBlockParameter,true);EthBlock ethBlock =null;try{
            ethBlock = request.send();//返回值 - 区块对象System.out.println(ethBlock.getBlock());}catch(IOException e){
            e.printStackTrace();}return ethBlock;}/**
     * 输入密码创建地址
     * @param password 密码(建议同一个平台的地址使用一个相同的,且复杂度较高的密码)
     * @return 地址hash
     */@OverridepublicServiceResponsenewAccount(String password){returnServiceResponse.createSuccessResponse("",createAccount());}/**
     * 根据hash值获取交易
     * @param hash
     * @return
     * @throws IOException
     */publicstaticEthTransactiongetTransactionByHash(String hash)throwsIOException{Request<?,EthTransaction> request = web3j.ethGetTransactionByHash(hash);return request.send();}/**
     * 账户解锁,使用完成之后需要锁定
     * @param address
     * @return
     * @throws IOException
     */publicBooleanlockAccount(String address)throwsIOException{Request<?,BooleanResponse> request = geth.personalLockAccount(address);BooleanResponse response = request.send();return response.success();}/**
     * 解锁账户,发送交易前需要对账户进行解锁
     * @param address 地址
     * @param password 密码
     * @param duration 解锁有效时间,单位秒
     * @return
     * @throws IOException
     */publicBooleanunlockAccount(String address,String password,BigInteger duration)throwsIOException{Request<?,PersonalUnlockAccount> request = admin.personalUnlockAccount(address, password, duration);PersonalUnlockAccount account = request.send();return account.accountUnlocked();}/**
     * 发送交易并获得交易hash值
     * @param transaction
     * @param password
     * @return
     * @throws IOException
     */publicStringsendTransaction(Transaction transaction,String password)throwsIOException{Request<?,EthSendTransaction> request = admin.personalSendTransaction(transaction, password);EthSendTransaction ethSendTransaction = request.send();return ethSendTransaction.getTransactionHash();}/**
     *  获取钱包里的所有用户
     * @return
     */@AutowiredpublicList<String>getAllAccounts(){List<String> list =newArrayList<String>();try{Request<?,EthAccounts> request = geth.ethAccounts();
            list = request.send().getAccounts();System.out.println(list.toString());}catch(IOException e){// TODO Auto-generated catch block
            e.printStackTrace();}return  list;}/**
     * 钱包地址余额是否足够转账校验
     * @param bigDecimalValue
     * @param addressBalance
     * @return
     */publicstaticStringcheckMoney(String bigDecimalValue,String addressBalance){if(newBigDecimal(addressBalance).subtract(newBigDecimal(bigDecimalValue)).compareTo(newBigDecimal("0"))<=0){System.out.println("转账金额大于钱包地址余额");return"转账金额大于钱包地址余额";}else{System.out.println("=======================");return"";}}}

6、Controller类AccountController

/**
 * @author deray.wang
 * @date 2019/11/27 17:16
 */@Slf4j@Api(value ="用户账号接口", tags ="用户账号接口")@RestController@RequestMapping(CommonConst.API_PATH_VERSION_1+"/account")publicclassAccountController{@AutowiredprivateBlockchainService blockchainService;@RequestMapping(value ="/newAccount", method =RequestMethod.POST)@ApiOperation(httpMethod ="POST", value ="创建地址", produces =MediaType.APPLICATION_JSON_VALUE)publicServiceResponsenewAccount(@ApiParam(name ="password")@RequestParam(name ="password")String password){return blockchainService.newAccount(password);}@RequestMapping(value ="/getAccount", method =RequestMethod.GET)@ApiOperation(httpMethod ="GET", value ="获取钱包里的所有用户", produces =MediaType.APPLICATION_JSON_VALUE)publicServiceResponsegetAllAccounts(){List<String> accounts = blockchainService.getAllAccounts();returnServiceResponse.createSuccessResponse("",accounts);}}

大部分操作已经实现。有需要的可以联系我沟通。剩下的操作,给区块链处理,比如转账确认。


本文转载自: https://blog.csdn.net/qq_25987725/article/details/141450739
版权归原作者 cesske 所有, 如有侵权,请联系我们删除。

“Spring Boot项目中集成Geth与以太坊区块链进行交互操作实例”的评论:

还没有评论