本节课为实战内容,手把手教大家如何通过 Java 语言来进行以太坊常见接口的调用,课程重要内容为代码配合文字描述,希望大家在学习的过程中同步进行练习,才能达到最好的效果。
开发准备事项
启动 dev 模式的 Geth
此操作在第 02 课中已经详细介绍过了,可以参考其内容进行 Geth 节点启动,也算是对之前内容的复习,这里不再赘述操作步骤。
开发环境准备
- JDK 版本 1.8(本教程使用,也可以使用 1.7、1.6 等其他版本);
- 开发工具 Intellij idea(本教程使用,也可以使用 Eclipse 等其他 IDE);
- Maven 版本管理(本教程使用,要依赖一些通用的 jar 包,采用 Maven 项目管理模式,也可自行下载依赖 jar 包使用其他方式,建议用 Maven 来操作)。
项目实战
创建 Maven 项目
在 idea 中单击创建新项目,进入如下页面,选择 Maven 项目:
单击 Next 按钮,填写 Maven 项目中的 GroupId 和 ArtifactId:
单击 Next 按钮,设置项目名称和项目路径:
单击 Finish 按钮,这样就创建好了一个 Maven 项目:
添加依赖
在 pom.xml 文件中新增依赖父元素和 Maven 编译指定 jdk 版本:
<dependencies>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
然后在其中父依赖中添加一下依赖信息:
<dependency>
<groupId>org.web3jgroupId>
<artifactId>coreartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>org.web3jgroupId>
<artifactId>gethartifactId>
<version>3.2.0version>
dependency>
上面引入的是一套开源的支持 Web3j 调用以太坊的依赖核心包和 Geth 操作包。Web3j 开源框架提供了一套 Java 版本的操作封装,让开发人员可以方便快捷的进行 JSON-RPC 的调用。
公共代码示例
创建 package:com.secbro.eth,并在 package 下面创建 EthService 类,后面针对 ETH 的操作,都会封装在 EthService 中做为静态方法呈现,类内容如下,后面代码讲解不再粘贴类内容,直接展示使用到的方法:
package com.secbro.eth;
/**
* @author zzs
*/
public class EthService {
/**
* geth节点可调用的json-rpc接口地址和端口
*/
private static final String URL = "http://127.0.0.1:8545/";
}
其中需要注意的是上面的静态变量为 Geth 节点启动的 JSON-RPC 访问的地址和端口,如果不是本地启动可修改 IP 和端口,确保网络畅通。
初始化 Web3j、Geth 和 Admin 接口
Web3j 框架中提供了多类操作,我们重点介绍 Web3j、Geth 和 Admin 这三个接口,其中 Web3j 和 Admin 都在核心包 core 中,Geth 是在 Geth 包中,这三个接口与我们讲的 JSON-RPC 中说到的操作有对应的关系,其中 Web3j 接口封装了常见的 net、eth、db 等操作,与官方提供的操作一一对应。
Admin 接口对应的是 personal 的一部分操作:
- personalListAccounts()
- personalNewAccount(String password)
- personalUnlockAccount( String address, String passphrase, BigInteger duration)
- personalUnlockAccount( String address, String passphrase)
- personalSendTransaction( Transaction transaction, String password)
Geth 接口继承了 Admin 接口,并新增了以下操作:
- personalImportRawKey(String keydata, String password)
- personalLockAccount(String accountId)
- personalSign(String message, String accountId, String password)
- personalEcRecover(String message, String signiture)
下面在 EthService 类中新增如下实例化方法,来获得这三个接口的实现:
/**
* 初始化web3j普通api调用
*
* @return web3j
*/
public static Web3j initWeb3j() {
return Web3j.build(getService());
}
/**
* 初始化personal级别的操作对象
* @return Geth
*/
public static Geth initGeth(){
return Geth.build(getService());
}
/**
* 初始化admin级别操作的对象
* @return Admin
*/
public static Admin initAdmin(){
return Admin.build(getService());
}
/**
* 通过http连接到geth节点
* @return
*/
private static HttpService getService(){
return new HttpService(URL);
}
代码上的注释已经明确说明每个方法的用户,这里值得一提的是这里采用的是 HttpService 来初始化一个与 geth 通信的 http 连接,在真实生产中可以采用 OkHttpClient 来对此链接进行设置超时时间等参数。
通过上面的步骤就完成了三个接口对应的类的实例化操作,后面的内容就可以直接使用它们提供的方法来实现进行相应的业务处理。
常用接口使用示例
下面提供一些常用的接口(开发钱包或交易所)的具体实现的代码和解析。
创建地址
根据输入的密码创建地址,对应 personal_newAccount
操作,返回地址 hash:
/**
* 输入密码创建地址
*
* @param password 密码(建议同一个平台的地址使用一个相同的,且复杂度较高的密码)
* @return 地址hash
* @throws IOException
*/
public static String newAccount(String password) throws IOException {
Admin admin = initAdmin();
Request, NewAccountIdentifier> request = admin.personalNewAccount(password);
NewAccountIdentifier result = request.send();
return result.getAccountId();
}
查询区块高度
查询区块高度,对应eth_blockNumber
操作,返回当前区块高度。
/**
* 获得当前区块高度
*
* @return 当前区块高度
* @throws IOException
*/
public static BigInteger getCurrentBlockNumber() throws IOException {
Web3j web3j = initWeb3j();
Request, EthBlockNumber> request = web3j.ethBlockNumber();
return request.send().getBlockNumber();
}
解锁账户
解锁账户,发送交易前需要对账户进行解锁,对应personal_unlockAccount
操作。
/**
* 解锁账户,发送交易前需要对账户进行解锁
*
* @param address 地址
* @param password 密码
* @param duration 解锁有效时间,单位秒
* @return
* @throws IOException
*/
public static Boolean unlockAccount(String address, String password, BigInteger duration) throws IOException {
Admin admin = initAdmin();
Request, PersonalUnlockAccount> request = admin.personalUnlockAccount(address, password, duration);
PersonalUnlockAccount account = request.send();
return account.accountUnlocked();
}
锁定账户
账户解锁,使用完成之后需要锁定,对应操作personal_lockAccount
。
/**
* 账户解锁,使用完成之后需要锁定
*
* @param address
* @return
* @throws IOException
*/
public static Boolean lockAccount(String address) throws IOException {
Geth geth = initGeth();
Request, BooleanResponse> request = geth.personalLockAccount(address);
BooleanResponse response = request.send();
return response.success();
}
获取交易信息
根据 hash 值获取交易信息,对应操作eth_getTransactionByHash
。
/**
* 根据hash值获取交易
*
* @param hash
* @return
* @throws IOException
*/
public static EthTransaction getTransactionByHash(String hash) throws IOException {
Web3j web3j = initWeb3j();
Request, EthTransaction> request = web3j.ethGetTransactionByHash(hash);
return request.send();
}
查询区块内容
根据区块编号查询区块内容,对应操作eth_getBlockByNumber
。
/**
* 获得ethblock
*
* @param blockNumber 根据区块编号
* @return
* @throws IOException
*/
public static EthBlock getBlockEthBlock(Integer blockNumber) throws IOException {
Web3j web3j = initWeb3j();
DefaultBlockParameter defaultBlockParameter = new DefaultBlockParameterNumber(blockNumber);
Request, EthBlock> request = web3j.ethGetBlockByNumber(defaultBlockParameter, true);
EthBlock ethBlock = request.send();
return ethBlock;
}
发送交易
发送交易并获得交易 hash 值,对应操作personal_sendTransaction
。
/**
* 发送交易并获得交易hash值
*
* @param transaction
* @param password
* @return
* @throws IOException
*/
public static String sendTransaction(Transaction transaction, String password) throws IOException {
Admin admin = initAdmin();
Request, EthSendTransaction> request = admin.personalSendTransaction(transaction, password);
EthSendTransaction ethSendTransaction = request.send();
return ethSendTransaction.getTransactionHash();
}
获取指定地址 nonce
指定地址发送交易所需 nonce 获取,对应操作eth_getTransactionCount
。
/**
* 指定地址发送交易所需nonce获取
*
* @param address 待发送交易地址
* @return
* @throws IOException
*/
public static BigInteger getNonce(String address) throws IOException {
Web3j web3j = initWeb3j();
Request, EthGetTransactionCount> request = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.LATEST);
return request.send().getTransactionCount();
}
使用接口注意事项
在使用上面的接口调用时,request.send() 方法是同步返回结果,在某些情况下可能会导致响应比较慢,因此此框架提供了 request.sendAsync() 异步操作,当进行异步操作时只是将操作的执行发送出去,并没有获得相应的操作结果,需要通过监听器获取结果的通知。
此套课程中示例的代码大家在真实生产环境使用时需要进行相应的优化处理,比如网络优化、初始化链接优化、超时时间设定、冷钱包设置、私钥与 Geth 节点分离、系统安全考虑等。
小结
本节课给大家介绍了 Web3j Java 版本的框架的基本使用,大家可根据本节课程的内容进行扩展性的练习,对其他 API 的使用进行尝试。