欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > Spring Boot项目中集成Geth与以太坊区块链进行交互操作实例

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

2024/10/24 23:25:18 来源:https://blog.csdn.net/qq_25987725/article/details/141450739  浏览:    关键词: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*/
@Configuration
public class EthConfig {@Value("${web3j.client-address}")private String rpc;@Beanpublic Web3j web3j() {OkHttpClient.Builder builder = new OkHttpClient.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(new HttpService(rpc,httpClient,false));return web3j;}/*** 初始化admin级别操作的对象* @return Admin*/@Beanpublic Admin admin() {OkHttpClient.Builder builder = new OkHttpClient.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(new HttpService(rpc,httpClient,false));return admin;}/*** 初始化personal级别操作的对象* @return Geth*/@Beanpublic Geth geth() {OkHttpClient.Builder builder = new OkHttpClient.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(new HttpService(rpc,httpClient,false));return geth;}}

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

/*** 转账ETH参数类* @author*/
@Data
@ApiModel
@ToString
public class TransferEchBean {@ApiModelProperty("fromAddr")private String fromAddr;@ApiModelProperty("密码")private String privateKey;@ApiModelProperty("toAddr")private String toAddr;@ApiModelProperty("amount")private BigDecimal amount;@ApiModelProperty("data")private String data;}
/*** @author deray.wang* @date 2024/04/20 13:44*/
@Data
public class BlockchainTransaction {private String id;//发送发件人IDprivate Integer fromId;//交易金额private long value;//收件人IDprivate Integer toId;private Boolean accepted;
}

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

BlockchainService.java类


/*** @author deray.wang* @date 2024/04/20 13:36*/
public interface BlockchainService {/*** 获取账户的Nonce* @param web3j* @param addr* @return*/BigInteger getAcountNonce(Web3j web3j, String addr);/*** 获取账户余额* @param web3j* @param addr* @return*/BigDecimal getAccountBalance(Web3j web3j, String addr);/*** 查询区块内容* @param web3j* @param blockNumber* @return*/EthBlock getBlockEthBlock(Web3j web3j, BigInteger blockNumber);/*** 创建钱包* @param password* @return*/ServiceResponse newAccount(String password);/*** 地址列表* @return*/List<String> getAllAccounts();/*** 转账ETH* @param web3j* @param fromAddr* @param privateKey* @param toAddr* @param amount* @param data* @return*/ServiceResponse transferETHD(Web3j web3j, TransferEchBean filterBean);/*** 普通转账ETH* @param web3j* @param filterBean* @return*/ServiceResponse tranETH(Web3j web3j, TransferEchBean filterBean);

实现类:BlockchainServiceImpl.java

/*** @author deray.wang* @date 2024/11/20 13:52*/
@Service
public class BlockchainServiceImpl implements BlockchainService {private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainService.class);private static BigInteger gNoce = null;@Value("${wallet.file}")private String FILE;@Autowiredprivate Admin admin;@Autowiredprivate static Web3j web3j;@Autowiredprivate Geth geth;/*** 获取账户的Nonce* @param web3j* @param addr* @return*/@Overridepublic  BigInteger getAcountNonce(Web3j web3j, String addr) {return getNonce(web3j,addr);}@Overridepublic BigDecimal getAccountBalance(Web3j web3j, String addr) {return getBalance(web3j,addr);}/****/@Overridepublic ServiceResponse transferETHD(Web3j web3j, TransferEchBean filterBean){//封装业务参数Map<String,String> map = new HashMap<String,String>();map.put("time", String.valueOf(new Date()));map.put("type","info");map.put("msg","Web3 Test!!!000000000000000000000000000");JSONObject jsonObj=new JSONObject(map);//将data转化为hexString datahex = null;try {datahex = HexUtils.toHexString(jsonObj.toString().getBytes("UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}return transferETH(web3j,filterBean.getFromAddr(),filterBean.getPrivateKey(),filterBean.getToAddr(),filterBean.getAmount(),datahex);}/****/@Overridepublic ServiceResponse tranETH(Web3j web3j, TransferEchBean filterBean){。。return ServiceResponse.createFailResponse("",0,"");}/*** 指定地址发送交易所需nonce获取* @param web3j* @param addr* @return*/public static  BigInteger getNonce(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*/public static BigInteger getTokenBalance(Web3j web3j, String fromAddress, String contractAddress) {String methodName = "balanceOf";List<Type> inputParameters = new ArrayList<>();List<TypeReference<?>> outputParameters = new ArrayList<>();Address address = new Address(fromAddress);inputParameters.add(address);TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {};outputParameters.add(typeReference);Function function = new Function(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*/ServiceResponse transferETH(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(new BigDecimal(val))) < 0) {//throw new RuntimeException("余额不足,请核实");return ServiceResponse.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(new BigInteger("1"));//RawTransaction.createEtherTransaction(nonce,gasPrice,gasLimit,to,value);if (privateKey.startsWith("0x")){privateKey = privateKey.substring(2);}ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(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);return ServiceResponse.createFailResponse("",0,"交易失败:"+message);}} catch (InterruptedException e) {System.out.println("transaction failed,info:" + ethSendTransaction.getError().getMessage());e.printStackTrace();return ServiceResponse.createFailResponse("",0,"交易失败:"+ethSendTransaction.getError().getMessage());} catch (ExecutionException e) {System.out.println("transaction failed,info:" + ethSendTransaction.getError().getMessage());e.printStackTrace();return ServiceResponse.createFailResponse("",0,"交易失败:"+ethSendTransaction.getError().getMessage());}}String transactionHash = ethSendTransaction.getTransactionHash();if(ethSendTransaction.hasError()){String message=ethSendTransaction.getError().getMessage();return ServiceResponse.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 = new HashMap<String,String>();mapRes.put("txHash", transactionHash);return ServiceResponse.createSuccessResponse("",mapRes,"交易成功,等待记账!");}}/*** 获取ETH余额* @param web3j* @param address* @return*/public static BigDecimal getBalance(Web3j web3j, String address) {try {EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();//单位转换BigDecimal banlance = Convert.fromWei(new BigDecimal(ethGetBalance.getBalance()),Convert.Unit.ETHER);return banlance;} catch (IOException e) {e.printStackTrace();//throw new Exception("查询钱包余额失败");return null;}}public static BigInteger getTransactionGasLimit(Web3j web3j, Transaction transaction) {try {EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();if (ethEstimateGas.hasError()){throw new RuntimeException(ethEstimateGas.getError().getMessage());}return ethEstimateGas.getAmountUsed();} catch (IOException e) {throw new RuntimeException("net error");}}/*** generate a random group of mnemonics* 生成一组随机的助记词*/private String generateMnemonics() {byte[] initialEntropy = new byte[16];new SecureRandom().nextBytes(initialEntropy);String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);return mnemonic;}private Map<String,String> createAccount() {//创建Map对象Map<String, String> map = new HashMap<String,String>();       //数据采用的哈希表结构Bip39Wallet wallet = null;// 创建一个存放keystore的文件夹String path = FILE;try {// 创建钱包wallet = WalletUtils.generateBip39Wallet("", new File(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*/@Overridepublic EthBlock getBlockEthBlock(Web3j web3j,BigInteger blockNumber){DefaultBlockParameter defaultBlockParameter = new DefaultBlockParameterNumber(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*/@Overridepublic ServiceResponse newAccount(String password) {return ServiceResponse.createSuccessResponse("",createAccount());}/*** 根据hash值获取交易* @param hash* @return* @throws IOException*/public static EthTransaction getTransactionByHash(String hash) throws IOException {Request<?, EthTransaction> request = web3j.ethGetTransactionByHash(hash);return request.send();}/*** 账户解锁,使用完成之后需要锁定* @param address* @return* @throws IOException*/public  Boolean lockAccount(String address) throws IOException {Request<?, BooleanResponse> request = geth.personalLockAccount(address);BooleanResponse response = request.send();return response.success();}/*** 解锁账户,发送交易前需要对账户进行解锁* @param address 地址* @param password 密码* @param duration 解锁有效时间,单位秒* @return* @throws IOException*/public Boolean unlockAccount(String address, String password, BigInteger duration) throws IOException{Request<?, PersonalUnlockAccount> request = admin.personalUnlockAccount(address, password, duration);PersonalUnlockAccount account = request.send();return account.accountUnlocked();}/*** 发送交易并获得交易hash值* @param transaction* @param password* @return* @throws IOException*/public  String sendTransaction(Transaction transaction, String password) throws IOException {Request<?, EthSendTransaction> request = admin.personalSendTransaction(transaction, password);EthSendTransaction ethSendTransaction = request.send();return ethSendTransaction.getTransactionHash();}/***  获取钱包里的所有用户* @return*/@Autowiredpublic List<String> getAllAccounts() {List<String> list = new ArrayList<String>();try {Request<?, EthAccounts> request = geth.ethAccounts();list = request.send().getAccounts();System.out.println(list.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return  list;}/*** 钱包地址余额是否足够转账校验* @param bigDecimalValue* @param addressBalance* @return*/public static String checkMoney(String bigDecimalValue,String addressBalance){if(new BigDecimal(addressBalance).subtract(new BigDecimal(bigDecimalValue)).compareTo(new BigDecimal("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")
public class AccountController {@Autowiredprivate BlockchainService blockchainService;@RequestMapping(value = "/newAccount", method = RequestMethod.POST)@ApiOperation(httpMethod = "POST", value = "创建地址", produces = MediaType.APPLICATION_JSON_VALUE)public ServiceResponse newAccount(@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)public ServiceResponse getAllAccounts() {List<String> accounts = blockchainService.getAllAccounts();return ServiceResponse.createSuccessResponse("",accounts);}
}

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com