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>
                                    <!-- filter out signature files from signed dependencies, else repackaging fails with security ex -->
                                    <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;

/**
 * @Author: Mae
 * @Date: 2022/4/18
 * @Time: 12:27
 * @Description: 用户信息
 */
@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;

/**
 * @Author: Mae
 * @Date: 2022/4/18
 * @Time: 10:33
 * @Description: 保险合约
 */
@Contact(name = "insure")
@Default
public class InsureChaincode implements ContractInterface {

    /**
     * 利率
     */
    private final Double LV = 0.8d;

    public InsureChaincode() {
    }

    /**
     * 增加一个用户
     *
     * @param cxt
     * @param user
     * @return
     */
    @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]}'

四、到最后了

👆👆👆👆👆👆👆 最后,希望大家都能够测试成功!!!👆👆👆👆👆👆