利用趣链作为底链,通过java智能合约进行数据上链
介绍
本java项目举例说明如何将person数据上链及获取链上数据。完整代码:https://github.com/forReak/hyperTest
用趣链官网得Hyperchain,开源版本,作为底链,搭建一个节点。
底链地址:https://github.com/hyperchain/hyperchain
底链相关文档:https://docs.hyperchain.cn/docs/flato-solo/5.1-flato-sdk-litesdk
用本地虚拟机搭建一个节点,然后将数据存储到节点得合约中。以及通过合约获取合约中得数据
一、搭建节点
去官网下载 https://github.com/hyperchain/hyperchain/releases
用docker得话直接创建镜像

本地虚拟机安装后

注意端口是8081,节点部署完成
二、创建java项目--pom.xml
java项目即是智能合约,将项目打成jar包部署到链上,之后通过api调用链上得智能合约进行保存读取数据。注意,数据是持久化到合约中得。
新建maven项目,pom文件如下,注意打包方式为jar,构建方式是普通jar包构建,mainclass需要配置成自己得继承 BaseContract类 得类。后面会讲到。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.furao</groupId>
<artifactId>hyperTest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.hyperchain</groupId>
<artifactId>litesdk</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>
org.legalperson.MyContractImpl
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
三、创建person类
定义一个人类,后面需要将个人信息传入到链上
package org.legalperson;
import java.util.Date;
public class Person {
String id ;
String name;
Integer age;
String copAddr;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCopAddr() {
return copAddr;
}
public void setCopAddr(String copAddr) {
this.copAddr = copAddr;
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", copAddr='" + copAddr + '\'' +
'}';
}
}
四、创建 MyContract.java 接口类
合约接口类描述这个合约能干什么。比如当前代码中,有俩方法,一个是保存数据,一个是读取数据。注意合约接口类需要继承 BaseContractInterface
package org.legalperson;
import cn.hyperchain.contract.BaseContractInterface;
public interface MyContract extends BaseContractInterface {
String saveData(Person person);
String getData(String id);
}
五、创建合约 InvokeMyContract.java 合约调用类
这个类为调用合约得入口。当把合约部署到链上得时候,就通过这个类进行数据传输,把数据发给链上得合约。
注意:
1、这个类需要 实现 BaseInvoke 。并且 实现一个invoke方法。invoke方法的入参和返回值就是 BaseInvoke 的泛型,第一个是合约得返回值,第二个是合约得入参。
也就是说这个合约不管怎么样,只能通过invoke方法进行 入参和返回参数。相当于main方法。
2、所以合约的入参和返回值需要考虑通用。比如在本代码中,invoke方法里的调用的两个方法都返回了string类型。一个是保存数据,一个是读取数据。
3、需要有一个空构造函数
4、需要有一个所有入参的构造函数,用于接收入参。因为数据是通过构造函数来传输的。
package org.legalperson;
import cn.hyperchain.contract.BaseInvoke;
public class InvokeMyContract implements BaseInvoke<String, MyContract> {
public int function;
public Person person;
public String id;
public InvokeMyContract() {
}
public InvokeMyContract(int function, Person person, String id) {
this.function = function;
this.person = person;
this.id = id;
}
@Override
public String invoke(MyContract myContract) {
if(function == 1){
return myContract.saveData(person);
}
else{
return myContract.getData(id);
}
}
}
六、合约实现类 MyContractImpl.java
有了定义,有了入口,则需要创建合约实现类,来实现合约的功能。
需要注意:
1、实现类需要继承 BaseContract 实现 第四步的接口实现合约接口中的功能。
2、链上数据是保存在合约中的持久化变量中的,因此需要在合约中定义一个变量,用注解 @StoreField 进行标识持久化字段
3、实现接口中的方法。本代码中就是将数据保存到变量里。以及读取变量中的数据。
package org.legalperson;
import cn.hyperchain.annotations.StoreField;
import cn.hyperchain.core.HyperMap;
import cn.hyperchain.contract.BaseContract;
public class MyContractImpl extends BaseContract implements MyContract{
@StoreField
public HyperMap<String, Person> personMap = new HyperMap<String, Person>();
public MyContractImpl() {
}
@Override
public String saveData(Person person) {
personMap.put(person.getId(),person);
return "true";
}
@Override
public String getData(String id) {
return personMap.get(id).toString();
}
}
七、打包
打成普通jar包,需要一个mainclass,指向你的合约实现类。
maven的配置如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>
org.legalperson.MyContractImpl
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
打包完成后,在target 文件夹中出现jar,jar包结构如下

八、部署合约,让合约上链
(1)创建测试类 TestPerson
注意里面的变量,一个jar包路径,一会儿需要上链的,一个虚拟机ip,装链节点的,一个合约链上地址。先为空。一会儿合约上链后,需要手动更改这个地址。
public class TestPerson {
//合约jar包路径
String jarPath = "C:\\Users\\furao\\IdeaProjects\\hyperTest\\hyperTest\\target\\hyperTest-1.0-SNAPSHOT.jar";
String defaultURL = "192.168.204.130:8081";
String addr = "";
}
(2)添加获取节点管理器
根据官网文档描述,需要创建一个方法,用于链接节点。在TestPerson类中添加此方法
/**
* 获取节点管理器
* @return
*/
public ProviderManager getProviderManager(){
//负责管理与节点的连接
DefaultHttpProvider defaultHttpProvider = new DefaultHttpProvider.Builder().setUrl(defaultURL).build();
//rovideManager负责集成、管理这些HttpProvider
return ProviderManager.createManager(defaultHttpProvider);
}
(3)添加部署合约方法
原理是把合约jar包通过文件流的形式传到链上。按官网文档来写。如果你的合约写的有问题,部署合约时这个方法会报错。在TestPerson类中添加此方法
public String deploy() throws IOException, RequestException {
InputStream is = FileUtil.readFileAsStream(jarPath);
ProviderManager providerManager = getProviderManager();
ContractService contractService = ServiceManager.getContractService(providerManager);
AccountService accountService = ServiceManager.getAccountService(providerManager);
Account account = accountService.genAccount(Algo.ECRAW);
//部署合约
Transaction transaction = new Transaction.HVMBuilder(account.getAddress()).deploy(is).build();
transaction.sign(account);
TxHashResponse send = contractService.deploy(transaction).send();
ReceiptResponse receiptResponse = send.polling();
//获取合约地址
String contractAddress = receiptResponse.getContractAddress();
System.out.println("contract address: " + contractAddress);
return contractAddress;
}
(3)添加 main 方法
在TestPerson类中添加此方法
public static void main(String[] args) throws IOException, RequestException {
TestPerson testPerson = new TestPerson();
String addr = testPerson.deploy();
}
(4)执行main方法,部署合约
手动将打印的合约地址保存到addr变量里。这里其实合约已经部署完成了。这个地址就是合约在链上的地址。永远不会没有,改变。

(5)调用合约 -- 存储数据
添加调用合约 setPerson 方法。注意调用合约的时候,invoke里传入合约地址,以及你的合约入口类 InvokeMyContract ,并把数据通过构造函数的形式传递过去。这里通过构造把person 张三传过去了。然后构造的第一个参数,当是1时执行保存,当是其他,执行读取。参见 InvokeMyContract 的 invoke 方法
public void setPerson() throws RequestException {
ProviderManager providerManager = getProviderManager();
ContractService contractService = ServiceManager.getContractService(providerManager);
AccountService accountService = ServiceManager.getAccountService(providerManager);
Account account = accountService.genAccount(Algo.ECRAW);
Person zhangsan = new Person();
zhangsan.setId("001");
zhangsan.setAge(18);
zhangsan.setName("张三");
zhangsan.setCopAddr("地址");
//创建指定invoke bean的交易
Transaction transaction = new Transaction.HVMBuilder(account.getAddress()).invoke(addr, new InvokeMyContract(1,zhangsan,"")).build();
transaction.sign(account);
ReceiptResponse receiptResponse1 = contractService.invoke(transaction).send().polling();
//对交易执行结果进行解码
String decodeHVM = Decoder.decodeHVM(receiptResponse1.getRet(), String.class);
System.out.println("decode: " + decodeHVM);
}
main方法稍微修改,
public static void main(String[] args) throws IOException, RequestException {
TestPerson testPerson = new TestPerson();
//String addr = testPerson.deploy();
testPerson.setPerson();
//testPerson.getPErson();
}
执行main方法,调用合约,将数据上链,看到已经返回 true
(6)调用合约 -- 获取合约数据
其实同(5)中的setPerson方法。唯一区别是
new InvokeMyContract(2,p1,"001")
中传了2,获取数据
public void getPErson() throws RequestException {
ProviderManager providerManager = getProviderManager();
ContractService contractService = ServiceManager.getContractService(providerManager);
AccountService accountService = ServiceManager.getAccountService(providerManager);
Account account = accountService.genAccount(Algo.ECRAW);
Person p1 = new Person();
//创建指定invoke bean的交易
Transaction transaction = new Transaction.HVMBuilder(account.getAddress()).invoke(addr, new InvokeMyContract(2,p1,"003")).build();
transaction.sign(account);
ReceiptResponse receiptResponse1 = contractService.invoke(transaction).send().polling();
//对交易执行结果进行解码
String decodeHVM = Decoder.decodeHVM(receiptResponse1.getRet(), String.class);
System.out.println("decode: " + decodeHVM);
}
执行main方法如下
可见已经从map中获取了存储的数据


本java项目举例说明如何将person数据上链及获取链上数据。完整代码:https://github.com/forReak/hyperTest
浙公网安备 33010602011771号