Hyperledger Fabric使用Java自定义智能合约
一、前提
首先要搭建好fabric测试网络
搭建方法:进去看看吧 https://blog.csdn.net/weixin_51799151/article/details/123315317?spm=1001.2014.3001.5502
二、我的主要文件目录
三、开始啦
1、启动测试网络
进入 test-network 目录下,./network.sh up 启动成功之后会生成一个 orderer节点 和两个 peer节点
2、创建通道
./network.sh createChannel [-c 通道名]
不写通道名 默认为 mychannel
3、配置环境变量
export FABRIC_PATH=/fabric/scripts/fabric-samples
export FABRIC_CFG_PATH=${FABRIC_PATH}/config/
export MSP_PATH=${FABRIC_PATH}/test-network/organizations
export CORE_PEER_TLS_ENABLED=true
export PATH=${FABRIC_PATH}/bin:$PATH
FABRIC_PATH 根据自己的实际路径修改
4、合约代码
配置远程仓库和javasdk和各种插件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atmae.fabric</groupId>
<artifactId>smart-contract</artifactId>
<name>smart-contract</name>
<version>1.0-SNAPSHOT</version>
<description>fabric智能合约</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<fabric-chaincode-java.version>2.4.1</fabric-chaincode-java.version>
<fastjson.version>1.2.79</fastjson.version>
<logback.version>1.2.0</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<repositories>
<repository>
<id>central</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://www.jitpack.io</url>
</repository>
<repository>
<id>artifactory</id>
<url>https://hyperledger.jfrog.io/hyperledger/fabric-maven</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven China Mirror</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.hyperledger.fabric-chaincode-java</groupId>
<artifactId>fabric-chaincode-shim</artifactId>
<version>${fabric-chaincode-java.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>chaincode</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.hyperledger.fabric.contract.ContractRouter</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
合约数据对象:使用@DataType 注解标识,字段使用 @Property 注解标识
package com.atmae.fabric.entity;
import org.hyperledger.fabric.contract.annotation.DataType;
import org.hyperledger.fabric.contract.annotation.Property;
import java.util.Objects;
@DataType
public class User{
@Property
private final Long id;
@Property
private Double money;
public Long getId() {
return id;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public User(Long id, Double money) {
this.id = id;
this.money = money;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(money, user.money);
}
@Override
public int hashCode() {
return Objects.hash(id, money);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", money=" + money +
'}';
}
}
package com.atmae.fabric;
import com.alibaba.fastjson.JSON;
import com.atmae.fabric.entity.InsureDeal;
import com.atmae.fabric.entity.User;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contact;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
@Contact(name = "insure")
@Default
public class InsureChaincode implements ContractInterface {
private final Double LV = 0.8d;
public InsureChaincode() {
}
@Transaction(intent = Transaction.TYPE.SUBMIT)
public String addUser(final Context cxt, final User user) {
ChaincodeStub stub = cxt.getStub();
String personJson = JSON.toJSONString(user);
stub.putStringState(String.valueOf(user.getId()), personJson);
return stub.getTxId();
}
@Transaction(intent = Transaction.TYPE.EVALUATE)
public User getUser(final Context ctx, final String userId) {
ChaincodeStub stub = ctx.getStub();
String userJSON = stub.getStringState(userId);
if (userJSON == null || userJSON.isEmpty()) {
String errorMessage = "对不起,当前用户不存在";
throw new ChaincodeException(errorMessage);
}
return JSON.parseObject(userJSON, User.class);
}
合约逻辑: 合约类使用 @Contract 与 @Default 注解标识并实现 ContractInterface 接口
合约方法使用 @Transaction 注解标识
Transaction.TYPE.SUBMIT 为 「写入交易」
Transaction.TYPE.EVALUATE 为 「查询」
5、打包合约源代码
我的合约源代码放在chaincode目录下
peer lifecycle chaincode package insure.tar.gz --path ../chaincode/insure --lang java --label insure
查看:
6、安装链码
在指定 peer 节点上安装链码。 peer0.org1:
设置环境:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
安装链码:
peer lifecycle chaincode install insure.tar.gz
看的蓝色就特别开心 😃😃
peer0.org2: 设置环境:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
安装链码:
peer lifecycle chaincode install insure.tar.gz
7、查看已安装的合约清单
peer lifecycle chaincode queryinstalled
记住(复制就行了呗):package-id
8、审批合约
org1:
设置环境:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
审批合约:
peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--tls\
--cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--channelID mychannel \
--name insure\
--version 1.0 \
--package-id insure:4c8dce2c7f746d26293ca8f27a3ccdec3b4b38091f873f40f8ac9508c02029bc \
--sequence 1
别忘了!修改package-id!
org2: 设置环境:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
审批合约:
peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--tls \
--cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--channelID mychannel \
--name insure \
--version 1.0 \
--package-id insure:4c8dce2c7f746d26293ca8f27a3ccdec3b4b38091f873f40f8ac9508c02029bc \
--sequence 1
别忘了!修改package-id!
9、合约提交检查
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name insure --version 1.0 --sequence 1 --output json
都说true说明成功
10、向通道提交合约
peer lifecycle chaincode commit \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--tls \
--cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--channelID mychannel \
--name insure \
--peerAddresses localhost:7051 \
--tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
--peerAddresses localhost:9051 \
--tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
--version 1.0 \
--sequence 1
查看通道上已经提交的合约清单:
peer lifecycle chaincode querycommitted --channelID mychannel --name insure --output json
11、测试啦
invoke参数较多,我们建一个脚本命令 vim invoke.sh
peer chaincode invoke -o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--tls \
--cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-C mychannel \
-n insure \
--peerAddresses localhost:7051 \
--tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
--peerAddresses localhost:9051 \
--tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
-c ${1}
添加数据,查询数据: 连接其中一个节点,设置环境:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
增、查
sh invoke.sh '{"function":"addUser","Args":[1,200]}'
peer chaincode query -C mychannel -n insure -c '{"Args":["getUser", 1]}'
四、到最后了
👆👆👆👆👆👆👆 最后,希望大家都能够测试成功!!!👆👆👆👆👆👆
|