hyperledger fabric_Fabric-java-sdk实践
1 准备条件2 操作流程2.1 添加依赖2.2 将需要的文件放到合适的位置2.3 修改文件2.4 编写代码2.5 提取contract对象2.6 方法调用3 常见错误4 注意背景:目前我们的fabric服务使用node搭建的。目的:在spring-boot中实现与使用node-sdk搭建fabric服务一样的功能,其中wallet是使用node脚本生成的,当然应该可以直接使用java程序生成,后面
1 准备条件
- 2 操作流程
- 2.1 添加依赖
- 2.2 将需要的文件放到合适的位置
- 2.3 修改文件
- 2.4 编写代码
- 2.5 提取contract对象
- 2.6 方法调用
- 3 常见错误
- 4 注意
背景:目前我们的fabric
服务使用node
搭建的。
目的:在spring-boot
中实现与使用node-sdk
搭建fabric
服务一样的功能,其中wallet
是使用node
脚本生成的,当然应该可以直接使用java
程序生成,后面有时间尝试一下。
环境:测试的时候使用的是单机,docker
,fabric1.4.4
,solo
模式
1 准备条件
- 确保
fabric
安装成功,/home/go/src/github.com/hyperledger/fabric-samples/
下的first-network
例子运行没问题 - 确保你自己的
fabric
区块链启动成功 - 确保
spring-boot
项目可用
2 操作流程
2.1 添加依赖
修改pom.xml
文件,添加
<dependency>
<groupId>org.hyperledger.fabric</groupId>
<artifactId>fabric-gateway-java</artifactId>
<version>1.4.4</version>
</dependency>
2.2 将需要的文件放到合适的位置
在spring-boot
项目中controller
目录下新增一个目录sdk
(随便)
- 将服务器上的
connection-org1.json
文件复制到sdk
目录中 - 将与
connection-org1.json
同目录的crypto-config
目录下的crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
文件复制到sdk
目录中 - 把
/home/go/src/github.com/hyperledger/fabric-samples/fabcar/java/src/main/java/org/example
目录下的三个文件复制到sdk
目录
2.3 修改文件
- 修改
connection-org1.json
文件,将其中的localhost
,和grpcs
中peer*.org*.example.com
等字段改为服务器的ip
地址 - 修改
EnrollAdmin.java
和RegisterUser.java
文件
// 证书相关文件
props.put("pemFile","src/main/java/com/***/web/controller/sdk/ca.org1.example.com-cert.pem");
// ca客户端
HFCAClient caClient = HFCAClient.createNewInstance("https://ip地址:7054", props);
// wallet目录,不修改的话会在项目根目录
Wallet wallet = Wallet.createFileSystemWallet(Paths.get("src/main/java/com/***/web/controller/sdk/wallet"));
// ip地址
enrollmentRequestTLS.addHost("ip地址");
两个文件修改方法一样,其他的不用改
- 修改完成后分别运行
EnrollAdmin.java
和RegisterUser.java
不要忘了!
感觉sui kou yi shuo fabric1.4.4的java版本的SDK有点捞,注册管理员和用户的时候还需要传入ca.org1.example.com-cert.pem
,而node版本的SDK只需要传入connection-org1.json
,会自动读取其中的ca.org1.example.com
字段。
2.4 编写代码
先看一下官方给的案例: fabcar sample[1]
1.4版本官方文档
在sdk
目录中建一个SdkController
的java
文件(可以直接修改ClientApp.java
)
主要修改内容:
// path
Path walletDirectory = Paths.get("src/main/java/com/***/web/controller/sdk/wallet");
Path networkConfigFile = Paths.get("src/main/java/com/***/web/controller/sdk/connection-org1.json");
// your channel name and contract name
Network network = gateway.getNetwork("mychannel");
Contract contract = network.getContract("mycontract");
最主要的还是调用你的链码中的方法
2.5 提取contract对象
修改完上面的内容基本就可以进行方法的调用了,如果有多个方法的话总不能在每个方法中都读取连接文件和认证文件吧。
所有我们需要把contract
对象提取出来,当然也可以提取gateway
或network
对象,不过这样就比较麻烦,没必要。
// 返回contract对象
public Contract getContract() throws IOException{
// Load an existing wallet holding identities used to access the network.
Path walletDirectory = Paths.get("src/main/java/com/***/web/controller/sdk/wallet");
Wallet wallet =Wallet.createFileSystemWallet(walletDirectory);
System.out.println(wallet.get("user1"));
// Path to a common connection profile describing the network.
System.out.println("读取连接文件开始");
Path networkConfigFile = Paths.get("src/main/java/com/***/web/controller/sdk/connection-org1.json");
System.out.println(networkConfigFile);
System.out.println("读取连接文件结束");
// Configure the gateway connection used to access the network.
Gateway.Builder builder = Gateway.createBuilder()
.identity(wallet,"user1")
.networkConfig(networkConfigFile)
.discovery(true);
// Create a gateway connection
Gateway gateway = builder.connect();
// Obtain a smart contract deployed on the network.
Network network = gateway.getNetwork("mychannel");
System.out.println(network.getChannel());
Contract contract1 = network.getContract("mycontract");
System.out.println(contract1);
return contract1;
}
为什么我在这儿定义一个contract1
,因为我已经定义了一个全局类属性Contract contract=this.getContract();
2.6 方法调用
import com.alibaba.fastjson.JSON;
import com.bicon.web.controller.SuperController;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
//fabric-java-sdk
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.ContractException;
import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.gateway.Wallet
@RestController
@RequestMapping(path = "/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class SdkController extends SuperController{ //这个继承和下面的getParameterMap有关
Contract contract=this.getContract();
public SdkController() throws IOException {
}
// 方法说明
@RequestMapping("/test")
public Object test(HttpServletRequest request) throws IOException, InterruptedException, TimeoutException, ContractException {
Map<String, Object> params = getParameterMap(request);
// 去掉“-”
String Id= UUID.randomUUID().toString().replaceAll("-","");
String param=JSON.toJSONString(params);
System.out.println(param);
// 提交交易 “test”为链码中的方法,紧接着的为链码中需要的参数
byte[] result=contract.submitTransaction("test", param);
String resultStr=new String(result, StandardCharsets.UTF_8);
System.out.println(resultStr);
HashMap resultMap=JSON.parseObject(resultStr,HashMap.class);
return resultMap;
}
}
然后就可以发送请求进行测试了
http://127.0.0.1:8080/test
//application/json
{
...
}
3 常见错误
错误1 validatePeerResponses: invalid response from peer grpcs://localhost:10051
解决方法:修改hosts
(C:WINDOWSSystem32driversetchosts
)文件,添加映射如下:
*.*.*.* peer1.org1.example.com
*.*.*.* peer0.org1.example.com
*.*.*.* ca_peerOrg1
*.*.*.* ca_peerOrg2
*.*.*.* peer1.org2.example.com
*.*.*.* peer0.org2.example.com
*.*.*.* orderer.example.com
其中*.*.*.*
为服务器的ip地址
错误2
解决方法:
- 修改hosts文件
- 删掉下面的代码
static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
}
错误3
09:06:51.004 [http-nio-18090-exec-1] ERROR WEB-RESPONSE-MONITOR -
org.hyperledger.fabric.gateway.GatewayRuntimeException: org.hyperledger.fabric.sdk.exception.InvalidArgumentException: Channel mychannel has been shutdown.
启发[2]
try (Gateway gateway = builder.connect()) {
...
} catch (ContractException | TimeoutException e) {
e.printStackTrace();
}
发现try-catch
里面的代码可以正常执行,而其他方法中的代码却不能执行。删除try-catch
即可
4 注意
- 不能多次执行注册管理员和用户的代码
- 每次重启区块链后
connection-org1.json
,crypto-config
目录都会发生变化,需要重新注册管理员和用户,也就是生成wallet
目录 - 注意我的目录结构,大家的可能会有些区别
- 注意其中的*** 符号
- 项目目录结构
参考资料
[1] Fabric Gateway SDK for Java: https://github.com/hyperledger/fabric-gateway-java/
[2] Channel mychannel has been shutdown: https://stackoverflow.com/questions/58861348/hyperledger-fabric-async-parallel-transaction-using-java-gateway-sdk
更多推荐
所有评论(0)