当前位置: 代码网 > 科技>区块链>动态 > Hyperledger Fabric 2.x 自定义智能合约

Hyperledger Fabric 2.x 自定义智能合约

2024年08月04日 动态 我要评论
![file](https://oscimg.oschina.net/oscnet/up-8bd561235c802a81a99433d873eeebdcac6.png) ## 一、说明 为了持续地进行信息的更新,以及对账本进行管理(写入交易,进行查询等),区块链网络引入了智能合约来实现对账本的访问和控制;智能合约在 Fabric 中称之为 `链码`,是区块链应用的业务逻辑。 本文分享如何使用 Java 语言开发智能合约,以及合约的安装与使用。 ## 二、环境准备 1、部署好 `Fabric` 的测试网络,按照上一篇...

file

一、说明

为了持续地进行信息的更新,以及对账本进行管理(写入交易,进行查询等),区块链网络引入了智能合约来实现对账本的访问和控制;智能合约在 fabric 中称之为 链码,是区块链应用的业务逻辑。

本文分享如何使用 java 语言开发智能合约,以及合约的安装与使用。

 

二、环境准备

1、部署好 fabric 的测试网络,按照上一篇文章《hyperledger fabric 2.x 环境搭建》的内容执行第1至5步

- 启动好两个 peer 节点和一个 orderer 节点
- 创建好 mychannel 通道

file

2、在环境变量中配置好执行命令(bin)、配置(config)与msp文件夹的路径: 执行 vim /etc/profile 添加以下内容:

export fabric_path=/opt/gopath/src/github.com/hyperledger/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

file

 

三、下载合约代码

gitee:https://gitee.com/zlt2000_admin/my-fabric-chaincode-java

github:https://github.com/zlt2000/my-fabric-chaincode-java

 

四、代码解析

fabric 2.x 版本后的合约编写方式与旧版本略有不同,需要实现 contractinterface 接口,下面是官方的一段说明:

4.1. 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 指定 mainclassorg.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>

 

4.2. model

创建合约的数据对象 user 使用 @datatype 注解标识,定义三个字段 useridnamemoney 使用 @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;
    }
}

 

4.3. 合约逻辑

  1. 合约类使用 @contract@default 注解标识,并实现 contractinterface 接口
  2. 合约方法使用 @transaction 注解标识
  3. 包含3个交易方法:initaddusertransfer
  4. 包含2个查询方法:getuserqueryall
@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", "zlt",100d);
        adduser(ctx, "2", "admin",200d);
        adduser(ctx, "3", "guest",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));
    }
}

 

五、打包合约代码

把合约源代码打包成压缩文件,用于后续安装:

peer lifecycle chaincode package mycc.tar.gz --path /opt/app/my-fabric-chaincode-java --lang java --label mycc

 

六、安装合约

在指定 peer 节点上安装链码,下面分别为两个机构安装。

6.1. 为机构peer0.org1安装合约

执行以下命令,设置 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 mycc.tar.gz

成功后返回:

2022-02-09 22:09:13.498 est 0001 info [cli.lifecycle.chaincode] submitinstallproposal -> installed remotely: response:<status:200 payload:"\nemycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae\022\004mycc" > 
2022-02-09 22:09:13.498 est 0002 info [cli.lifecycle.chaincode] submitinstallproposal -> chaincode code package identifier: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae

 

6.2. 为机构peer0.org2安装合约

执行以下命令,设置 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 mycc.tar.gz

成功后返回:

2022-02-09 22:14:14.862 est 0001 info [cli.lifecycle.chaincode] submitinstallproposal -> installed remotely: response:<status:200 payload:"\nemycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae\022\004mycc" > 
2022-02-09 22:14:14.862 est 0002 info [cli.lifecycle.chaincode] submitinstallproposal -> chaincode code package identifier: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae

查看安装的合约清单:

peer lifecycle chaincode queryinstalled

返回合约的 package idlabel

installed chaincodes on peer:
package id: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae, label: mycc

 

七、审批合约

当合约安装后,需经过机构的审批达成一致后才允许使用。  

7.1. 为机构peer0.org1审批合约定义

执行以下命令,设置 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 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 mycc \
  --version 1.0 \
  --package-id mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae \
  --sequence 1

成功后返回:

2022-02-09 22:22:38.403 est 0001 info [chaincodecmd] clientwait -> txid [2531db2811945a641947000cb15cfd19e0b72da594dfba994f5f79b6bc51bce2] committed with status (valid) at localhost:7051

 

7.2. 为机构peer0.org2审批合约定义

执行以下命令,设置 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 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 mycc \
  --version 1.0 \
  --package-id mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae \
  --sequence 1

成功后返回:

2022-02-09 22:22:47.711 est 0001 info [chaincodecmd] clientwait -> txid [796a1e0a735e69425bcd5911bdf4b2a8003bbac977c5e60c769f84da6b86ef86] committed with status (valid) at localhost:9051

 

7.3. 合约提交检查

检查合约的审批情况,是否可以向通道进行提交:

peer lifecycle chaincode checkcommitreadiness --channelid mychannel --name mycc --version 1.0 --sequence 1 --output json

返回:

{
	"approvals": {
		"org1msp": true,
		"org2msp": true
	}
}

 

八、提交合约

执行以下命令,向通道提交合约:

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 mycc \
  --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

成功后返回:

2022-02-09 22:22:57.445 est 0001 info [chaincodecmd] clientwait -> txid [97ded758675113b9339dc9b378a13c0790ea3780855bb8f651758bfb007fc1ec] committed with status (valid) at localhost:7051
2022-02-09 22:22:57.456 est 0002 info [chaincodecmd] clientwait -> txid [97ded758675113b9339dc9b378a13c0790ea3780855bb8f651758bfb007fc1ec] committed with status (valid) at localhost:9051

查看通道上已经提交的合约:

peer lifecycle chaincode querycommitted --channelid mychannel --name mycc --output json

返回:

{
	"sequence": 1,
	"version": "1.0",
	"endorsement_plugin": "escc",
	"validation_plugin": "vscc",
	"validation_parameter": "eiavq2hhbm5lbc9bchbsawnhdglvbi9fbmrvcnnlbwvuda==",
	"collections": {},
	"approvals": {
		"org1msp": true,
		"org2msp": true
	}
}

 

九、测试智能合约

  1. 交易数据使用 peer chaincode invoke [flags] 命令,该命令将尝试向网络提交背书过的交易。
  2. 查询数据使用 peer chaincode query [flags],该命令不会生成交易。

由于 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 mycc \
  --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}

9.1. 初始化账本

执行以下命令,调用合约的 init 方法初始化3条账本记录:

sh invoke.sh '{"function":"init","args":[]}'

 

9.2. 查询数据

执行以下命令,设置 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

执行下面命令,调用 queryall 方法,查询所有数据:

peer chaincode query -c mychannel -n mycc -c '{"args":["queryall"]}'

执行后返回3条数据的数组:

[{"money":100.0,"name":"zlt","userid":"1"},{"money":200.0,"name":"admin","userid":"2"},{"money":300.0,"name":"guest","userid":"3"}]

 

执行下面命令,调用 getuser 方法传入 1 参数,查询单个数据:

peer chaincode query -c mychannel -n mycc -c '{"args":["getuser", "1"]}'

执行后返回id为1的数据:

{"money":100,"name":"zlt","userid":"1"}

 

9.3. 新增数据

执行以下命令,调用 adduser 方法,新增一条id为4的记录:

sh invoke.sh '{"function":"adduser","args":["4","test","400"]}'

 

9.4. 转账

执行以下命令,调用 transfer 方法,进行转账操作:

sh invoke.sh '{"function":"transfer","args":["4","1","400"]}'

转账成功后,使用查询命令进行查看:

peer chaincode query -c mychannel -n mycc -c '{"args":["queryall"]}'

file

 

扫码关注有惊喜!

file

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com