Hyperledger Fabric - 链码部署
前提条件
启动测试网络
./network-myself.sh up
创建通道
./network-myself.sh createChannel
智能合约(链码)
pom.xml文件
配置远程仓库
<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>
依赖合约sdk
<dependency>
<groupId>org.hyperledger.fabric-chaincode-java</groupId>
<artifactId>fabric-chaincode-shim</artifactId>
<version>${fabric-chaincode-java.version}</version>
</dependency>
通过插件 maven-shade-plugin 指定 mainClass 为 org.hyperledger.fabric.contract.ContractRouter
新版本所有合约的 mainClass 都为 org.hyperledger.fabric.contract.ContractRouter
<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>
model
创建合约的数据对象 User 使用 @DataType 注解标识,定义三个字段 userId、name、money 使用 @Property 注解标识:
@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;
}
}
合约逻辑
合约类使用 @Contract 与 @Default 注解标识,并实现 ContractInterface 接口
合约方法使用 @Transaction 注解标识
Transaction.TYPE.SUBMIT 为「写入交易」
Transaction.TYPE.EVALUATE 为 「查询」包含3个交易方法:init、addUser、transfer
包含2个查询方法:getUser、queryAll
@Contract(name = "mycc")
@Default
public class MyAssetChaincode implements ContractInterface {
public MyAssetChaincode() {}
/**
* 初始化3条记录
*/
@Transaction(intent = Transaction.TYPE.SUBMIT)
public void init(final Context ctx) {
addUser(ctx, "1", "chenyi",100D);
addUser(ctx, "2", "admin",200D);
addUser(ctx, "3", "chenyi666",300D);
}
/**
* 新增用户
*/
@Transaction(intent = Transaction.TYPE.SUBMIT)
public User 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 user;
}
/**
* 查询某个用户
*/
@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);
}
User user = JSON.parseObject(userJSON, User.class);
return user;
}
/**
* 查询所有用户
*/
@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 void 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));
}
}
链码安装及部署
模拟参数
export CHANNEL_NAME="mychannel"
export CC_NAME=mycc
export CC_SRC_PATH=../chaincode
export CC_SRC_LANGUAGE=java
export CC_VERSION="1.0"
export CC_SEQUENCE="1"
export CC_INIT_FCN=""
export CC_END_POLICY=""
export CC_COLL_CONFIG=""
export DELAY="3"
export MAX_RETRY="5"
export VERBOSE="false"
export INIT_REQUIRED=""
export CC_RUNTIME_LANGUAGE=java
部分字段解释:
CHANNEL_NAME=${1:-"mychannel"} # 通道名称,默认mychannel
CC_NAME=${2} # 链码名称
CC_SRC_PATH=${3} # 智能合约代码所在的目录
CC_SRC_LANGUAGE=${4} # 智能合约语言(当前支持Go、Java、Javascript、Typescript)
CC_VERSION=${5:-"1.0"} # 链码版本,默认1.0
CC_SEQUENCE=${6:-"1"} # 链码被定义或者更新多少次的指数
CC_INIT_FCN=${7:-"NA"} # 链码初始化调用的函数名
CC_END_POLICY=${8:-"NA"} # 背书策略
DELAY=${10:-"3"} # 延迟执行时间(单位:秒)
MAX_RETRY=${11:-"5"} # 尝试最多失败次数
INIT_REQUIRED # 请求执行 Init 函数来初始化链码
设置环境变量
export FABRIC_CFG_PATH=/root/go/src/github.com/hyperledger/fabric-samples/config
export CORE_PEER_TLS_ENABLED=true
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export PEER0_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export PEER1_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
export PEER0_ORG2_CA=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export PEER0_ORG3_CA=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export ORDERER_ADMIN_TLS_SIGN_CERT=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
export ORDERER_ADMIN_TLS_PRIVATE_KEY=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key
打包链码
rm -rf mycc.tar.gz
peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME}_${CC_VERSION}
安装链码
在组织1中peer节点安装链码
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode install ${CC_NAME}.tar.gz
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER1_ORG1_CA
export CORE_PEER_ADDRESS=localhost:8051
peer lifecycle chaincode install ${CC_NAME}.tar.gz
在组织2中peer节点安装链码
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
peer lifecycle chaincode install ${CC_NAME}.tar.gz
查询组织2中peer节点安装链码的链码包ID
在组织1与组织2中安装链码的链码包ID是一样的,因此在一个组织中查看即可
peer lifecycle chaincode queryinstalled >&log.txt
export PACKAGE_ID=$(sed -n "/${CC_NAME}_${CC_VERSION}/{s/^Package ID: //; s/, Label:.*$//; p;}" log.txt)
echo $PACKAGE_ID
批准智能合约
组织1身份批准智能合约
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/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 "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG}
组织2身份批准智能合约
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/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 "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG}
检查通道成员是否已批准相同的链码定义
peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --sequence ${CC_SEQUENCE} --tls --cafile "$ORDERER_CA" --output json
将链码提交至通道
export PEER_CONN_PARMS=(--peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:8051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt)
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} "${PEER_CONN_PARMS[@]}" --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG}
若需要私有数据
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/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 "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} --collections-config /home/user/chaincode/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')"
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/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 "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} --collections-config /home/user/chaincode/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')"
export PEER_CONN_PARMS=(--peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:8051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt)
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} "${PEER_CONN_PARMS[@]}" --version ${CC_VERSION} --sequence ${CC_SEQUENCE} --collections-config /home/user/chaincode/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')"
查询提交的链码
查询组织1提交的链码
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME}
查询组织2提交的链码
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME}
调用链码函数
当前是调用的是组织2中的peer节点
初始化链码
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc ${PEER_CONN_PARMS[@]} -c '{"function":"init","Args":[]}'
查询所有资产
peer chaincode query -C mychannel -n mycc -c '{"Args":["queryAll"]}'
附加:
docker-compose down -v
docker volume ls
docker volume rm explorer_pgdata
docker volume rm explorer_walletstore
docker-compose up -d
docker ps -a
cd ~/.local/share/Trash/files/
Hyperledger Fabric - 链码部署的更多相关文章
- Hyperledger Fabric链码之三
在<Hyperledger Fabric链码之一>和<Hyperledger Fabric链码之二>中我们介绍了链码的定义,并通过dev网络测试了测试了自己编写的链码程序. 本 ...
- Hyperledger fabric 链码篇GO(四)
Hyperledger fabric 链码篇GO(四) fabric中的链码也就是我们区块链所认知的智能合约,fabric中可由nodejs,java,go编写,本篇只针对GO语言编写链码.将详细介绍 ...
- Hyperledger Fabric链码之一
什么是链码(Chaincode)? 我们知道区块链有3个发展阶段:区块链1.0,区块链2.0,区块链3.0.其中区块链2.0就是各种区块链平台百花齐放的阶段,区块链2.0最大的特点就是智能合约,我们接 ...
- Hyperledger Fabric链码之二
上篇文章中我们介绍了链码的概念,本文中我们将介绍Fabric下链码的编写和测试.我们会通过一个简单例子的方式来阐述链码API的使用. 链码API 每一个链码程序都必须实现一个接口Chainco ...
- hyperledger fabric部署总结
之前在有道云笔记上分享过,但想想还是搬到这里来吧,以后统一方便整理自己的知识进入正题.... 之前在调研 hyperledger fabric,其实部署说明官网都有,只是东西都是国外的照着操作也会遇到 ...
- Hyperledger Fabric服务器配置及修改Docker容器卷宗存储根目录/位置
Hyperledger Fabric节点服务器对存储空间的消耗还是比较大的,在我实际生产体验的过程中,每一条请求数据大概仅2K左右,但实际占用空间远不止这点,每个节点都会对Block及链进行保存维护, ...
- 002_HyperLedger Fabric安装部署
上一次我们把HyperLedger Fabric的环境全部搭建好了,下面开始正式的HyperLedger Fabric安装部署 首先需要安装编译工具gcc,用命令yum install -y gcc安 ...
- HyperLedger Fabric部署与链码解读
1.Fabric简介 Fabric是超级账本中的一个项目,用以推进区块链技术.和其他区块链类似,它也有一个账本,使用智能合约,且是一个参与者可以分别管理自身交易的系统.它是一个联盟链.Fabric与其 ...
- Hyperledger Fabric(4)链码ChainCode
智能合约,是一个抽象的概念,智能合约的历史可以追溯到 1990s 年代.它是由尼克萨博(Nick Szabo)提出的理念,几乎与互联网同龄. 我们这里所说的智能合约只狭义的指区块链中.它能够部署和运行 ...
- Hyperledger Fabric 部署
Hyperledger Fabric 部署 Hyperledger Fabric需要使用Docker.Go环境. Docker环境安装 Docker环境安装 直接查看这一篇,安装好之后将当前用户非ro ...
随机推荐
- 一级缓存和二级缓存--mybatis|hibernate
一级缓存和二级缓存的区别: 主要的不同是它们的作用范围不同. 一级缓存是session级别的. 也就是只有在同一个session里缓存才起作用,当这个session关闭后这个缓存就不存在了. 而二级缓 ...
- Robot Framework 自动化测试部署常见问题及处理方法(二)
书接上文 4.使用Open Browser关键字打开浏览器报错"WebDriverException: Message: 'geckodriver' executable needs to ...
- py并发编程
并发编程(并发,并行,同步,异步) 通俗理解并发编程中的相关核心概念 核心概念:进程.线程 CPU的作用 计算机的核心是CPU,它承担了所有的计算任务.它就像一座工厂,时刻在运行. CPU的核数(多核 ...
- CentOS7安装RabbitMQ (安装包安装)
环境: CentOS7 需要安装:erlang 22.2 rabbitmq 3.8.3 参考: rabbit官网地址:http://www.rabbitmq.com/which-erlang.htm ...
- Failed to start: app/proxyman/inbound: failed to listen TCP on 10808
问题现象 启动 v2xxx-With-Core 失败,报错信息如下: 2023/08/03 11:38:56 [Info] infra/conf/serial: Reading config: F:\ ...
- Hbuilder使用快捷键
Hbuilder的使用 1.Hbuilder基本操作设置基本外观文字大小,申请账号.2.Hbuilder快捷键- 新建菜单: ctrl + N- 新建: ctrl + N- 关闭: ctrl ...
- DeepSeek-R1的“思考”艺术,你真的了解吗?
大家好~,这里是AI粉嫩特攻队!今天咱们来聊聊一个有趣的话题--DeepSeek-R1到底什么时候会"思考",什么时候又会选择"偷懒"? 最近有朋友问我:&qu ...
- WPF调用FishSpeech的Demo
写了一个FishSpeech的教程:使用FishSpeech进行语音合成推理 - 天命小猪 - 博客园 研究了一下如何调用服务器API,朗读文本. 经过调研,决定使用NAudio库播放音频.遇到了一些 ...
- 小米字体和思源宋体CSS调用使用指南
小米字体 //css引用 <link rel="stylesheet" href="https://font.sec.miui.com/font/css?famil ...
- PCI-5565PIO主要应用场景
PCI-5565PIO主要应用场景包括军事领域.工业自动化和控制系统.仿真与培训以及数据采集与分发.在军事领域,PCI-5565PIO可用于航空航天系统的飞行控制计算机.导航系统和传感器系统之间的 ...