汪晓明对区块链、以太坊的思考

记录创业、生活的所思所感,探讨去中心化思想,推动区块链的发展。

HPB70:HPB主网接入最佳实践之Java版

1、开始准备

1.1 学习门槛

  • 熟悉Java的基本语法和使用
  • 熟悉Java的IDE工具基本使用(Eclipse,IDEA)
  • 熟悉Git版本管理的基本语法和使用
  • 熟悉SpingBoot开发(本Demo提供的是基于Springboot的源码)
  • 熟悉区块链的基本原理
  • HPB主网如何介入请前往https://www.hpb.io/client%E4%BA%86%E8%A7%A3

1.2 环境准备

  • JDK1.8以上版本
  • Eclipse4.7或者以上
  • Eclipse安装了Git插件
  • Eclipse安装了Spring插件
  • HPB主网接入的JAVA SDK请前往这里地址下载:https://www.hpb.io/client
  • 因为是maven工程,会从网络中下载工程依赖的jar宝,所以需要机器处于联网状态

1.3 源码地址

1
https://github.com/loglos/web3-hpb-test.git

2、开始实践

2.1 通过Eclipse Import 演示代码

  • 打开Eclipse,通过菜单栏依次点击打开:File/Import/Git/Projects form Git/
  • 选择通过Clone URI导入,在导入界面,输入源码的Github地址;
  • 然后下一步,选择master主分支
  • 然后下一步指定代码下载存放的路径
  • 然后下一步,选择Import as general project
  • 下一步,指定工程名称,默认是Github源码工程的名称,可以不修改,点击完成。

2.2 改变代码工程的属性

  • 因为上传到Github的代码是不包括代码工程配置信息的,所以需要进行简单的调整。
  • 源码工程是基于maven的类型,可以右键工程代码,选择“configure/convert to maven projects”
  • 转换成功后,系统就能识别为maven类型的工程了
  • 提醒:转换为maven工程后,很多依赖的jar包都是通过pom.xml来配置管理的,同时如果本地没安装过maven的本地仓库,第一次会默认从远程的拉取依赖jar包,速度比较慢,如果工程报错,需要多尝试几次

2.3 源码关键配置信息说明

打开maven配置文件pom.xml

1
2
3
4
5
6
7
8
9
<!--
其他的配置不需要修改,这里说明下,引用的事HPB主网的JAVA版本SDK
-->
      <dependency>
          <groupId>io.hpb.web3</groupId>
          <artifactId>web3-hpb</artifactId>
          <version>1.0.0</version>
      </dependency>
      

打开application.properties文件,配置文件可以根据需要进行修改,也可以不进行修改。 这里罗列了重要的几个配置属性进行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#指定工程发布后访问名称
spring.application.name=HpbWeb3Test

#工程发布后的访问服务端口
server.port=9988
server.servlet.path=/
server.use-forward-headers=true
server.servlet.context-path=/HpbWeb3Test
server.servlet.application-display-name=hpb web3 test application
web3.admin-client=true
web3.httpTimeoutSeconds=600

#指定的连接HPB主网的RPC网址,这里是主网正式网的开放的RPC地址
#通过访问这个地址,可以进行通过Java操作RPC命令,直接和HPB主网进行交互。
web3.client-address=http://pub.node.hpb.io/

打开HpbWeb3Controller.java文件,这里是本次演示的具体代码,这里对关键代码进行下说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//注意这里,是引用了HPB主网提供的Java SDK包,里面封装了如何通过RPC方式与底层交互的方法
import io.hpb.web3.abi.datatypes.Address;
import io.hpb.web3.abi.datatypes.DynamicArray;
import io.hpb.web3.abi.datatypes.generated.Bytes32;
import io.hpb.web3.contract.HpbNodes;
import io.hpb.web3.crypto.Credentials;
import io.hpb.web3.crypto.RawTransaction;
import io.hpb.web3.crypto.WalletUtils;
import io.hpb.web3.protocol.admin.Admin;
import io.hpb.web3.protocol.core.DefaultBlockParameterName;
import io.hpb.web3.protocol.core.methods.response.HpbBlockNumber;
import io.hpb.web3.protocol.core.methods.response.HpbGetBalance;
import io.hpb.web3.protocol.core.methods.response.HpbGetTransactionCount;
import io.hpb.web3.protocol.core.methods.response.HpbGetTransactionReceipt;
import io.hpb.web3.protocol.core.methods.response.HpbSendTransaction;
import io.hpb.web3.protocol.core.methods.response.TransactionReceipt;
import io.hpb.web3.tuples.generated.Tuple4;
import io.hpb.web3.tx.ChainId;
import io.hpb.web3.tx.RawTransactionManager;
import io.hpb.web3.utils.Convert;
import io.hpb.web3.utils.Numeric;
import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("/")
public class HpbWeb3Controller{
    
    //输出日志
  private static Log log = LogFactory.getLog(HpbWeb3Controller.class);
  //超时时间
  private final long WEB3J_TIMEOUT = 3;
  
  //演示的HPB账号地址
  private final String contractAddr = "0x7be6aa25600feed355b79b6a4e14dcdb0bd529cb";
  
  //设置账号的余额单位为18GWEI
  private final BigInteger gasPrice = Convert.toWei("18", Convert.Unit.GWEI).toBigInteger();
  //设置默认的GasLimit
  private final BigInteger gasLimit = new BigInteger("95000000");
  
  
  @Autowired
  private Admin admin;
  @ApiOperation(value="通过根据交易hash查询交易收据",notes = "过根据交易hash查询交易收据"
          + " reqStrList [  参数1:交易hash]")
  @PostMapping("/QueryByHash")
  public List<Object> QueryByHash(@RequestBody List<String> reqStrList)throws Exception{
      List<Object> list=new ArrayList<Object>();
      if(reqStrList!=null&&reqStrList.size()>0) {
          String transactionHash = reqStrList.get(0);
          
          //初始化封装的Java SDK包里的方法;通过初始化HpbGetTransactionReceipt,可以获得指定账号的交易数据
          HpbGetTransactionReceipt receipt = admin.hpbGetTransactionReceipt(transactionHash).send();
          if(!receipt.hasError()) {
              TransactionReceipt transactionReceipt = receipt.getResult();
              if(transactionReceipt.isStatusOK()) {
                  list.add(transactionReceipt);
              }
          }
      }
      return list;
  }
  
  
  @ApiOperation(value="获得当前区块号",notes = "获得当前区块号")
  @PostMapping("/getCurrentBlock")
  public List<Object> getCurrentBlock()throws Exception{
      List<Object> list=new ArrayList<Object>();
      
      //初始化封装的Java SDK包里的方法;通过HpbBlockNumber获取当前区块的对象内容
      HpbBlockNumber blockNumber = admin.hpbBlockNumber().sendAsync().get(WEB3J_TIMEOUT, TimeUnit.MINUTES);
      list.add(blockNumber);
      return list;
  }
  @ApiOperation(value="获得当前账户的Nonce",notes = "获得当前账户的Nonce"
          + " reqStrList [ 参数1:账户地址;")
  @PostMapping("/getCurrentNonce")
  public List<Object> getCurrentNonce(@RequestBody List<String> reqStrList)throws Exception{
      List<Object> list=new ArrayList<Object>();
      if(reqStrList!=null&&reqStrList.size()>0) {
          String address =reqStrList.get(0);
          
          //初始化封装的Java SDK包里的方法;通过HpbGetTransactionCount获取当前账号Nonce
          //Nonce是账户的随机数:在一个账户中的防止多重交易的用途。
          HpbGetTransactionCount transactionCount = admin.hpbGetTransactionCount(address, 
                  DefaultBlockParameterName.PENDING).sendAsync().get(WEB3J_TIMEOUT, TimeUnit.MINUTES);
          BigInteger nonce = transactionCount.getTransactionCount();
          log.info(nonce);
          list.add(nonce);
      }
      return list;
  }
  
  
  
  @ApiOperation(value="获得当前账户的余额",notes = "获得当前账户的余额"
          + " reqStrList [ 参数1:账户地址; ]")
  @PostMapping("/getBalance")
  public List<Object> getBalance(@RequestBody List<String> reqStrList)throws Exception{
      List<Object> list=new ArrayList<Object>();
      if(reqStrList!=null&&reqStrList.size()>0) {
          String address =reqStrList.get(0);
          
          //初始化封装的Java SDK包里的方法;通过HpbGetBalance获取当前账号的余额。
          HpbGetBalance balance = admin.hpbGetBalance(address, DefaultBlockParameterName.LATEST).send();
          log.info(balance);
          list.add(balance);
      }
      return list;
  }
  
  
  
  @ApiOperation(value="发送交易",notes = "发送交易"
          + " reqStrList [ 参数1:账户keystore地址; 参数2:密码; 参数3:接收账户地址;参数4:转账金额;]")
  @PostMapping("/sendTransaction")
  public List<Object> sendTransaction(@RequestBody List<String> reqStrList)throws Exception{
      List<Object> list=new ArrayList<Object>();
      if(reqStrList!=null&&reqStrList.size()>3) {
          //获取指定账号的keystore
          String keystore =reqStrList.get(0);
          
          //获取用户输入发送交易的账号密码
          String password =reqStrList.get(1);
          
          //加载私钥对象
          Credentials credentials = WalletUtils.loadCredentials(password, keystore);
          
          //创建交易管理对象
          RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);
          
          //获取nonce
          HpbGetTransactionCount transactionCount = admin.hpbGetTransactionCount(credentials.getAddress(), 
                  DefaultBlockParameterName.PENDING).sendAsync().get(WEB3J_TIMEOUT, TimeUnit.MINUTES);
          
          BigInteger nonce = transactionCount.getTransactionCount();
          
          //获取目标地址
          String to =reqStrList.get(2);
          
          //转账金额
          String value =reqStrList.get(3);
          
          //创建交易对象
          RawTransaction rawTransaction = RawTransaction.createTransaction(
                  nonce,
                  gasPrice,
                  gasLimit,
                  to,
                  Convert.toWei(value, Convert.Unit.HPB).toBigInteger(),
                  "");
              
          //签名并发送交易
          HpbSendTransaction transaction = transactionManager.signAndSend(rawTransaction);
          log.info(transaction.getTransactionHash());
          list.add(transaction);
      }
      return list;
  }
  
  
  
  @ApiOperation(value="调用HpbNodes智能合约",notes = "调用HpbNodes智能合约"
          + " reqStrList [ 参数1:账户keystore地址; 参数2:密码]")
  @PostMapping("/invokeHpbNodes")
  public List<Object> invokeHpbNodes(@RequestBody List<String> reqStrList)throws Exception{
      List<Object> list=new ArrayList<Object>();
      if(reqStrList!=null&&reqStrList.size()>1) {
          String keystore =reqStrList.get(0);
          String password =reqStrList.get(1);
          Credentials credentials = WalletUtils.loadCredentials(password, keystore);
          RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);
          HpbNodes hpbNodes = HpbNodes.load(contractAddr, admin, transactionManager, gasPrice, gasLimit);
          //调用智能合约
          Tuple4<DynamicArray<Address>, DynamicArray<Bytes32>, DynamicArray<Bytes32>, DynamicArray<Bytes32>> send = 
                  hpbNodes.getAllHpbNodes().send();
          Bytes32 bytes32 = send.getValue2().getValue().get(1);
          log.info(Numeric.toHexStringNoPrefix(bytes32.getValue()));
          Bytes32 bytes321 = send.getValue3().getValue().get(1);
          log.info(Numeric.toHexStringNoPrefix(bytes321.getValue()));
          Bytes32 bytes322 = send.getValue3().getValue().get(1);
          log.info(Numeric.toHexStringNoPrefix(bytes322.getValue()));
          list.add(send);
      }
      return list;
  }
  
}

2.4 运行程序和调试

1.发布程序

找到和选择好Web3HpbTestApplication类,右击”Run as /Spring Boot App” 启动成功后,系统会出现类似如下提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
2019-01-09 10:49:18.538 HpbWeb3Test [main] INFO  io.hpb.web3.Web3HpbTestApplication Caller+0  at org.springframework.boot.StartupInfoLogger.logStarting(StartupInfoLogger.java:50)
 - Starting Web3HpbTestApplication on jason-hpbdeMBP with PID 46179 (/Users/hpb2017/git/web3-hpb-test/target/classes started by hpb2017 in /Users/hpb2017/git/web3-hpb-test)
2019-01-09 10:49:18.540 HpbWeb3Test [main] DEBUG io.hpb.web3.Web3HpbTestApplication Caller+0   at org.springframework.boot.StartupInfoLogger.logStarting(StartupInfoLogger.java:53)
 - Running with Spring Boot v2.1.1.RELEASE, Spring v5.1.3.RELEASE
2019-01-09 10:49:18.542 HpbWeb3Test [main] INFO  io.hpb.web3.Web3HpbTestApplication Caller+0   at org.springframework.boot.SpringApplication.logStartupProfileInfo(SpringApplication.java:675)
 - No active profile set, falling back to default profiles: default
2019-01-09 10:49:20.818 HpbWeb3Test [main] INFO  i.h.w.c.Web3AutoConfiguration Caller+0    at io.hpb.web3.configure.Web3AutoConfiguration.admin(Web3AutoConfiguration.java:51)
 - Building admin service for endpoint: http://pub.node.hpb.io/
2019-01-09 10:49:21.406 HpbWeb3Test [main] INFO  i.h.w.c.Web3AutoConfiguration Caller+0    at io.hpb.web3.configure.Web3AutoConfiguration.Web3(Web3AutoConfiguration.java:42)
 - Building service for endpoint: http://pub.node.hpb.io/
2019-01-09 10:49:22.057 HpbWeb3Test [main] INFO  io.hpb.web3.Web3HpbTestApplication Caller+0   at org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:59)
 - Started Web3HpbTestApplication in 3.943 seconds (JVM running for 5.429)
 

2.测试验证接口

Springboot的接口可以通过自带的swagger插件进行访问和测试。 打开本地访问地址:http://localhost:9988/HpbWeb3Test/swagger-ui.html#/ 会显示如目前系统开放的测试接口,然后选择点击“hpb-web-3-controller”,接入该接口的具体方法测试界面。 点击“/getBalance”方法,点击“Try it out”开启编辑模式,输入你要查询余额的HPB地址;然后点击“Execute”开始执行。 系统会显示执行的结果,注意这里获取的余额显示的是科学计数法。 其他的接口可以依次根据接口的内容进行测试。

感谢HPB技术团队整理。

关于我

蓝莲花(汪晓明):HPB芯链(hpb.io)创始人,巴比特专栏作家。十余年金融大数据、区块链技术开发经验,曾参与创建银联大数据。主创区块链教学视频节目《明说》30多期,编写了《以太坊官网文档中文版》,并作为主要作者编写了《区块链开发指南》,在中国区块链社区以ID“蓝莲花”知名。2018年6月9日, HPB芯链荣登《2018胡润区块链企业排行榜》区块链创新企业TOP50。

公众号
小明微思考公众号

HPB

« HPB69:HPB JSON RPC API 介绍 HPB71:如何在HPB主网上发布智能合约 »