前置条件已经安装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);}}
大部分操作已经实现。有需要的可以联系我沟通。剩下的操作,给区块链处理,比如转账确认。
版权归原作者 cesske 所有, 如有侵权,请联系我们删除。