在 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"}]