胡豆秆

个人博客

欢迎来到我的个人博客~


Java智能合约部署及调用


Java智能合约部署及调用

​ 之前学习的Fabric智能合约及SDK都是使用Golang语言编写的,但做应用的同事使用的都是Java语言;为了打通这层语言上的隔阂,开始学习使用Java语言编写智能合约及SDK。

环境概述

  • 开发环境:Windows 10 家庭版、WSL2 - Ubuntu 20.04 LTS
  • 开发软件:eclipse
  • Java版本:1.8.0_191
  • 依赖管理:maven

区块链

​ 这里使用fabric-samples v1.4.4 版本的区块链网络,共识算法为 solo

智能合约

​ 这里使用eclipse作为开发环境,使用maven管理项目依赖,其它平台及项目依赖管理方法请自行摸索。

开发

在eclipse中新建一个maven项目

修改pom.xml文件,管理项目依赖

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hu</groupId>
	<artifactId>chaincode2</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.hyperledger.fabric-chaincode-java</groupId>
			<artifactId>fabric-chaincode-shim</artifactId>
			<!-- fabric版本 -->
			<version>1.4.0</version>
		</dependency>
	</dependencies>

	<build>
		<defaultGoal>compile</defaultGoal>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<!-- Java版本 -->
					<source>1.8</source>
					<target>1.8</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>com.hu.ChaincodeDemo</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>

启动类

ChaincodeDemo.java

package com.hu;

import java.util.List;
import org.bouncycastle.util.Strings;
import org.hyperledger.fabric.shim.ChaincodeBase;
import org.hyperledger.fabric.shim.ChaincodeStub;

public class ChaincodeDemo extends ChaincodeBase {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new ChaincodeDemo().start(args);
	}

	@Override
	public Response init(ChaincodeStub stub) {
		// TODO Auto-generated method stub
		// 获取初始化参数
		List<String> parameters = stub.getParameters();
		System.out.println("Chaincode init success. args: " + parameters.toString());
		return new Response(200, "success", null);
	}

	@Override
	public Response invoke(ChaincodeStub stub) {
		// TODO Auto-generated method stub
		//获取方法名
		String function = stub.getFunction();
		//获取参数
		List<String> parameters = stub.getParameters();
		System.out.println("function: " + function + " args:" + parameters.toString());
		return new Response(200, "success", Strings.toByteArray("链码调用成功"));
	}

}

尝试build一下,防止实例化链码时编译失败:右键项目 - Run As - Maven Build - Run 。

安装

​ 将项目复制到fabric-samples项目的chaincode目录下:

进入cli容器,fabric-samplescli 容器中已经设置好了一些默认的环境变量,追加设置一些环境变量方便操作:

hu@DESKTOP-5UBMFB7:~/goProject/fabricProject/fabric-samples/chaincode$ docker exec -it cli /bin/bash
root@792829121c3b:/opt/gopath/src/github.com/hyperledger/fabric/peer# echo $CORE_PEER_ADDRESS
peer0.org1.example.com:7051
root@792829121c3b:/opt/gopath/src/github.com/hyperledger/fabric/peer# echo $CORE_PEER_LOCALMSPID
Org1MSP
# orderer证书位置
root@792829121c3b:/opt/gopath/src/github.com/hyperledger/fabric/peer# export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

安装合约:peer chaincode install -n 合约名称 -v 合约版本号 -p 合约位置 -l 合约语言

root@792829121c3b:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n chaincode2 -v 1.0 -p /opt/gopath/src/github.com/chaincode/chaincode2/ -l java
2021-08-12 08:09:01.574 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2021-08-12 08:09:01.574 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2021-08-12 08:09:01.584 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >

实例化合约:peer chaincode instantiate -o orderer节点 --tls 是否启用tls --cafile 节点证书 -C 通道名 -n 合约名 -v 合约版本 -c 合约参数

root@792829121c3b:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n chaincode2 -v 1.0 -c '{"Args":["init"]}'
2021-08-12 08:20:07.810 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2021-08-12 08:20:07.810 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc

合约实例化时,会使用fabric-javaenv容器尝试编译合约。

编译日志

编译完成后,会有一个该合约的容器

ps:合约编译可能会花费很多时间,导致cli容器执行实例化合约命令时超时。如果实例化命令超时,可再次执行该实例化命令。

SDK

开发

​ 在eclipse中新建一个maven项目,将fabric-samples项目生成的身份证书(fabric-samples/first-network/crypto-config)复制到该项目下。

修改pom.xml文件,管理项目依赖

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.hu</groupId>
  <artifactId>sdkDemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
   <dependencies>
        <dependency>
            <groupId>org.hyperledger.fabric-sdk-java</groupId>
            <artifactId>fabric-sdk-java</artifactId>
            <version>2.0.0</version>
         </dependency>
     </dependencies>
</project>

新建一个User类,用于身份合约调用时的身份认证

User.java

package com.hu.entity;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.Set;

import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;

public class MyUser implements User {

	private String name;
	private String mspId;
	private Enrollment enrollment;

	public MyUser(String name, String mspId, String keyFile, String certFile) throws Exception {
		this.name = name;
		this.mspId = mspId;
		enrollment = loadFromPemFile(keyFile, certFile);
	}

	private Enrollment loadFromPemFile(String keyFile, String certFile) throws Exception {
		byte[] keyPem = Files.readAllBytes(Paths.get(keyFile)); // 载入私钥PEM文本
		byte[] certPem = Files.readAllBytes(Paths.get(certFile)); // 载入证书PEM文本
		CryptoPrimitives suite = new CryptoPrimitives(); // 载入密码学套件
		PrivateKey privateKey = suite.bytesToPrivateKey(keyPem); // 将PEM文本转换为私钥对象
		return new X509Enrollment(privateKey, new String(certPem)); // 创建并返回X509Enrollment对象
	}

	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}

	public Set<String> getRoles() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getAccount() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getAffiliation() {
		// TODO Auto-generated method stub
		return null;
	}

	public Enrollment getEnrollment() {
		// TODO Auto-generated method stub
		return enrollment;
	}

	public String getMspId() {
		// TODO Auto-generated method stub
		return mspId;
	}

}

新建一个启动类,进行合约调试

package com.hu.demo;

import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.HFClient;
import org.hyperledger.fabric.sdk.Channel;
import org.hyperledger.fabric.sdk.Peer;
import org.hyperledger.fabric.sdk.Orderer;
import org.hyperledger.fabric.sdk.security.CryptoSuite;

import com.hu.entity.MyUser;

import org.hyperledger.fabric.sdk.ChaincodeID;
import org.hyperledger.fabric.sdk.ChaincodeResponse;
import org.hyperledger.fabric.sdk.QueryByChaincodeRequest;
import org.hyperledger.fabric.sdk.ProposalResponse;
import org.hyperledger.fabric.sdk.TransactionProposalRequest;
import org.hyperledger.fabric.sdk.BlockEvent;
import org.hyperledger.fabric.sdk.BlockEvent.TransactionEvent;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;

public class Demo {
	public static void main(String[] args) throws Exception {
		// 创建User实例
		String keyFile = "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/e81bbfd91136ca08060ee3bd7ae8a104d916a8557a62722571ff801aacab41db_sk";
		String certFile = "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem";
		MyUser user;
		try {
			user = new MyUser("admin", "Org1MSP", keyFile, certFile);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("创建用户失败:" + e.toString());
			return;
		}
		// 创建HFClient实例
		HFClient client = HFClient.createNewInstance();
		try {
			client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
			client.setUserContext(user);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("创建客户端失败:" + e.toString());
			return;
		}

		// 创建通道实例
		Channel channel = client.newChannel("mychannel");
		// 设置节点信息
		Peer peer = client.newPeer("peer0.org1.example.com", "grpcs://localhost:7051",
				loadTLSFile("crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem",
						"peer0.org1.example.com"));
		try {
			channel.addPeer(peer);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("添加节点失败:" + e.toString());
			return;
		}
//		Peer peer1 = client.newPeer("peer1.org1.example.com", "grpcs://localhost:9051",
//				loadTLSFile("crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem",
//						"peer0.org2.example.com"));
//		channel.addPeer(peer1);
		Orderer orderer = client.newOrderer("orderer.example.com", "grpcs://localhost:7050",
				loadTLSFile("crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt",
						"orderer.example.com"));
		try {
			channel.addOrderer(orderer);
			channel.initialize();
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("添加节点失败:" + e.toString());
			return;
		}

		// 设置链码名
		ChaincodeID cid = ChaincodeID.newBuilder().setName("chaincode2").build();
		System.out.println("================================调用链码==============================");
		// 调用链码
		TransactionProposalRequest invoke = client.newTransactionProposalRequest();
		invoke.setChaincodeID(cid);
		invoke.setFcn("test");
		invoke.setArgs("test invoke");
		invoke.setProposalWaitTime(3000);
		try {
			Collection<ProposalResponse> transactionProposal = channel.sendTransactionProposal(invoke);
			for (ProposalResponse pres : transactionProposal) {
				if (pres.getStatus() != ChaincodeResponse.Status.SUCCESS) {
					System.out.println("链码调用失败:"+pres.getMessage());
					return;
				}
				System.out.println(pres.getProposalResponse().getResponse().getPayload().toStringUtf8());
			}
			TransactionEvent event = channel.sendTransaction(transactionProposal).get();
			System.out.format("交易ID: %s\n", event.getTransactionID());
		    System.out.format("有效性: %b\n", event.isValid());
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("链码调用失败:" + e.toString());
			return;
		}
	}

	private static Properties loadTLSFile(String rootTLSCert, String hostName) throws IOException {
		Properties properties = new Properties();
		properties.put("pemBytes", Files.readAllBytes(Paths.get(rootTLSCert)));
		properties.setProperty("sslProvider", "openSSL");
		properties.setProperty("negotiationType", "TLS");
		properties.setProperty("trustServerCertificate", "true");
		properties.setProperty("hostnameOverride", hostName);
		return properties;
	}
}

调试

​ 查看合约容器日志,执行启动类。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦