Bootstrap

Ubuntu22.04 fabric 2.5版本开发环境下开发、调试Java智能合约代码

Ubuntu22.04搭建fabric开发环境、开发环境下运行链码搭建fabric开发环境,运行了golang语言链码,这次运行使用广泛的Java语言链码。

还是基于fabric 2.5的版本。

Java 合约代码

User model

package tech.pplus.fabric.model;

import com.alibaba.fastjson.JSON;
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 String userId;

    @Property
    private final String name;

    @Property
    private final double money;

    public User(final String userId, final String name, final double money) {
        this.userId = userId;
        this.name = name;
        this.money = money;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if ((obj == null) || (getClass() != obj.getClass())) {
            return false;
        }
        User other = (User) obj;
        return Objects.deepEquals(
                new String[] {getUserId(), getName()},
                new String[] {other.getUserId(), other.getName()})
                &&
                Objects.deepEquals(
                        new double[] {getMoney()},
                        new double[] {other.getMoney()});
    }

    @Override
    public int hashCode() {
        return Objects.hash(getUserId(), getName(), getMoney());
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }

    public String getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }

    public double getMoney() {
        return money;
    }
}

FirstAssetChaincode(合约代码)

package tech.pplus.fabric;

import com.alibaba.fastjson.JSON;
import org.hyperledger.fabric.Logger;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contract;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeBase;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyModification;
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
import tech.pplus.fabric.model.User;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 智能合约
 *
 * @author Administrator
 */
@Default
@Contract(name = "FirstAssetChaincode")
public class FirstAssetChaincode implements ContractInterface {
    private final Logger logger = Logger.getLogger(FirstAssetChaincode.class);
    private Charset utf8 = StandardCharsets.UTF_8;

    public FirstAssetChaincode() {
    }
    /**
     * 初始化3条记录
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public void init(final Context ctx) {
        addUser(ctx, "1", "zl", 100D);
        addUser(ctx, "2", "admin", 200D);
        addUser(ctx, "3", "guest", 300D);
    }

    /**
     * 获取该id的所有变更记录
     */
    @Transaction(intent = Transaction.TYPE.EVALUATE)
    public String getHistory(final Context ctx, final String userId) {
        Map<String, String> userHistory = new HashMap<>();
        ChaincodeStub stub = ctx.getStub();
        QueryResultsIterator<KeyModification> iterator = stub.getHistoryForKey(userId);
        for (KeyModification result : iterator) {
            userHistory.put(result.getTxId(), result.getStringValue());
        }
        return JSON.toJSONString(userHistory);
    }

    /**
     * 新增用户
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public String addUser(final Context ctx, final String userId, final String name, final double money) {
        ChaincodeStub stub = ctx.getStub();
        User user = new User(userId, name, money);
        String userJson = JSON.toJSONString(user);
        stub.putStringState(userId, userJson);
        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 = String.format("User %s does not exist", userId);
            throw new ChaincodeException(errorMessage);
        }
        return JSON.parseObject(userJSON, User.class);
    }

    /**
     * 查询所有用户
     */
    @Transaction(intent = Transaction.TYPE.EVALUATE)
    public String queryAll(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();
        List<User> userList = new ArrayList<>();
        QueryResultsIterator<KeyValue> results = stub.getStateByRange("", "");
        for (KeyValue result : results) {
            User user = JSON.parseObject(result.getStringValue(), User.class);
            System.out.println(user);
            userList.add(user);
        }
        return JSON.toJSONString(userList);
    }

    /**
     * 转账
     *
     * @param sourceId 源用户id
     * @param targetId 目标用户id
     * @param money    金额
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public String transfer(final Context ctx, final String sourceId, final String targetId, final double money) {
        ChaincodeStub stub = ctx.getStub();
        User sourceUser = getUser(ctx, sourceId);
        User targetUser = getUser(ctx, targetId);
        if (sourceUser.getMoney() < money) {
            String errorMessage = String.format("The balance of user %s is insufficient", sourceId);
            throw new ChaincodeException(errorMessage);
        }
        User newSourceUser = new User(sourceUser.getUserId(), sourceUser.getName(), sourceUser.getMoney() - money);
        User newTargetUser = new User(targetUser.getUserId(), targetUser.getName(), targetUser.getMoney() + money);
        stub.putStringState(sourceId, JSON.toJSONString(newSourceUser));
        stub.putStringState(targetId, JSON.toJSONString(newTargetUser));
        return stub.getTxId();
    }
}

pom.xml 构建文件

<?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>tech.pplus.fabric</groupId>
    <artifactId>first-fabric-chaincode-java</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-fabric-chaincode-java</name>
    <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.5.0</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>
    </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>

构建链码

修改Maven配置

由于Maven默认使用apache的仓库,速度比较慢,参考Ubuntu22.04 解决repo.maven.apache.org下载慢(替换Maven仓库为阿里云镜像)修改仓库镜像

构建链码

使用mvn指令打包程序:

$ mvn clean package -DskipTests

启动链码

CORE_CHAINCODE_LOGLEVEL=debug CORE_PEER_TLS_ENABLED=false CORE_CHAINCODE_ID_NAME=FirstAssetChaincode:1.0  java -jar target/chaincode.jar -peer.address 127.0.0.1:7052

Java的链码取名为 FirstAssetChaincode,在devmode模式下,CORE_CHAINCODE_ID_NAME取值必须为名称:版本形式。

127.0.0.1:7052 是chaincode连接peer的端口,127.0.0.1:7051 是peer服务接口。

启动时 chaincode会输出其他配置项,可根据实际修改:
在这里插入图片描述

批准并提交链码

# 批准链码
$ peer lifecycle chaincode approveformyorg  -o 127.0.0.1:7050 --channelID ch1 --name FirstAssetChaincode --version 1.0 --sequence 1 --signature-policy "OR ('SampleOrg.member')" --package-id FirstAssetChaincode:1.0

# 检查链码批准状态
$ peer lifecycle chaincode checkcommitreadiness -o 127.0.0.1:7050 --channelID ch1 --name FirstAssetChaincode --version 1.0 --sequence 1 --signature-policy "OR ('SampleOrg.member')"

# 提交链码
$ peer lifecycle chaincode commit -o 127.0.0.1:7050 --channelID ch1 --name FirstAssetChaincode --version 1.0 --sequence 1 --signature-policy "OR ('SampleOrg.member')" --peerAddresses 127.0.0.1:7051

# 检查链码提交状态
$ peer lifecycle chaincode querycommitted -o 127.0.0.1:7050 --channelID ch1 

初始化链码

$ CORE_PEER_ADDRESS=127.0.0.1:7051 peer chaincode invoke -o 127.0.0.1:7050 -C ch1 -n FirstAssetChaincode -c '{"Args":["init"]}' --isInit
Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to execute transaction cb06f55587e1ec56f1b451d18a8ac9c966edbad16d6a7f59338632f8653637e3: error sending: timeout expired while executing transaction"

很不幸,出错了,提示超时。

google了大半天,尝试了很多种解决办法都没解决问题(有些奔溃)。

坚持,没放弃,终于看到了有类似的异常

解决办法也很简单:
JDK环境改成JDK11(刚开始也是安装JDK8)。多版本管理和切换看另外的博客Ubuntu 22.04同时安装OpenJDK 8和21版本
在这里插入图片描述

运行链码

# 新增用户
CORE_PEER_ADDRESS=127.0.0.1:7051 peer chaincode invoke -o 127.0.0.1:7050 -C ch1 -n FirstAssetChaincode -c '{"function":"addUser","Args":["4","test","400"]}'
2024-08-13 16:41:54.537 CST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200 payload:"1c22b7e44ce7b29f457120145a305b9ce5c90f1bfe48aaf7c042cf2ca19aefa9"

# 查询用户
CORE_PEER_ADDRESS=127.0.0.1:7051 peer chaincode invoke -o 127.0.0.1:7050 -C ch1 -n FirstAssetChaincode -c '{"Args":["getUser","4"]}' 
2024-08-13 16:44:11.588 CST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200 payload:"{\"money\":400,\"name\":\"test\",\"userId\":\"4\"}" 

# 查询所有用户
CORE_PEER_ADDRESS=127.0.0.1:7051 peer chaincode query -C ch1 -n FirstAssetChaincode -c '{"Args":["queryAll"]}'
[{"money":100.0,"name":"zl","userId":"1"},{"money":200.0,"name":"admin","userId":"2"},{"money":300.0,"name":"guest","userId":"3"},{"money":400.0,"name":"test","userId":"4"}]


;