fabric-sdk-java demo(TLS-enabled)
使用fabric-sdk-java,实现向fabric区块链上存入数据摘要并查询最新的数据记录。
本实例使用单机fabric1.4.1网络,采用raft共识;拥有五个orderer节点,四个peer节点;使用docker部署;开启TLS;chaincode采用Java编写;fabric状态数据库为couchdb,使用了数据库索引;fabric-sdk-java依赖版本为1.4.1。
采用的fabric网络实例地址://download.csdn.net/download/weixin_43562234/12118778
采用https://blog.csdn.net/weixin_43562234/article/details/104053966这篇博文中的solo单机单节点网络,开启TLS也可以。
建议有一定fabric基础的同学食用。
借鉴IBM的相关说明:
https://developer.ibm.com/tutorials/hyperledger-fabric-java-sdk-for-tls-enabled-fabric-network/
如果使用过程中出现问题,可以在评论区留言。
注意事项
1.依赖
除需要添加必要的fabric-sdk-java的依赖之外,还需要添加以下依赖
<dependency> <groupId>io.netty</groupId> <artifactId>netty-tcnative</artifactId> <version>1.1.33.Fork15</version> <classifier>${os.detected.classifier}</classifier> <scope>compile</scope> <optional>true</optional> </dependency>
properties属性中添加:<os.detected.classifier>windows-x86_64</os.detected.classifier>
我的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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <java.version>1.8</java.version> <grpc.version>1.11.0</grpc.version> <httpclient.version>4.5.5</httpclient.version> <os.detected.classifier>windows-x86_64</os.detected.classifier> </properties> <groupId>com.richfit</groupId> <artifactId>fabric-sdk-java</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> <name>fabric-sdk-java</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.16</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.16</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hyperledger.fabric-sdk-java/fabric-sdk-java --> <dependency> <groupId>org.hyperledger.fabric-sdk-java</groupId> <artifactId>fabric-sdk-java</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!--<!– https://mvnrepository.com/artifact/io.netty/netty-tcnative –> <dependency> <groupId>io.netty</groupId> <artifactId>netty-tcnative</artifactId> <version>2.0.25.Final</version> </dependency>--> <!-- https://mvnrepository.com/artifact/io.netty/netty-tcnative --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-tcnative</artifactId> <version>1.1.33.Fork15</version> <classifier>${os.detected.classifier}</classifier> <scope>compile</scope> <optional>true</optional> </dependency> </dependencies> </project>
2.域名
windows中需要修改hosts文件,不能直接使用虚拟机ip
hosts位置:
我添加了以下内容
192.168.43.66 peer0.org1.richfit.com 192.168.43.66 peer1.org1.richfit.com 192.168.43.66 peer0.org2.richfit.com 192.168.43.66 peer1.org2.richfit.com 192.168.43.66 orderer.richfit.com 192.168.43.66 orderer0.richfit.com 192.168.43.66 orderer1.richfit.com 192.168.43.66 orderer2.richfit.com 192.168.43.66 orderer3.richfit.com 192.168.43.66 orderer4.richfit.com
## 代码部分
代码结构
代码正文
1.App.class
package com.richfit.fabric.tls; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.richfit.fabric.tls.Business; import org.hyperledger.fabric.sdk.BlockEvent.TransactionEvent; import org.hyperledger.fabric.sdk.*; import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; import org.hyperledger.fabric.sdk.exception.ProposalException; import org.hyperledger.fabric.sdk.security.CryptoSuite; import java.io.File; import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; /** * @author adder * @date 2020/1/20 15:43 */ public class App { private static SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) throws Exception { Configurations configurations = new Configurations(); HFClient client = HFClient.createNewInstance(); //load parameters String keyDir = configurations.loadConfigurations().getJsonObject("options").getString("privateKeyFolder"); String keyFile = getKeyFilesInDir(new File(keyDir)).toString(); String certFile = configurations.loadConfigurations().getJsonObject("options").getString("signedCert"); String userName = configurations.loadConfigurations().getJsonObject("options").getString("user_id"); String mspId = configurations.loadConfigurations().getJsonObject("options").getString("msp_id"); String channelName = configurations.loadConfigurations().getJsonObject("options").getString("channel_id"); String peerName = configurations.loadConfigurations().getJsonObject("options").getString("peer_server_hostname"); String peerURL = configurations.loadConfigurations().getJsonObject("options").getString("peer_url"); String ordererName = configurations.loadConfigurations().getJsonObject("options").getString("orderer_server_hostname"); String ordererURL = configurations.loadConfigurations().getJsonObject("options").getString("orderer_url"); //create user object FabricUser user = new FabricUser(userName, mspId, keyFile, certFile); //encryption suite client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite()); client.setUserContext(user); //create channel object Channel channel = client.newChannel(channelName); //create peer String peerTLSCert = configurations.loadConfigurations().getJsonObject("options").getString("peer_tls_cacerts"); Properties peer_properties = new Properties(); peer_properties.put("pemBytes", Files.readAllBytes(Paths.get(peerTLSCert))); peer_properties.setProperty("sslProvider", "openSSL"); peer_properties.setProperty("negotiationType", "TLS"); Peer peer = client.newPeer(peerName, peerURL,peer_properties); channel.addPeer(peer); /*//create EvenHub String event_url = "grpcs://peer0.org1.richfit.com:7051"; // ensure that port is of event hub EventHub eventHub = client.newEventHub(peerName, event_url, peer_properties); channel.addEventHub(eventHub);*/ //orderer String ordererTLSCert = configurations.loadConfigurations().getJsonObject("options").getString("orderer_tls_cacerts"); Properties orderer_properties = new Properties(); orderer_properties.put("pemBytes", Files.readAllBytes(Paths.get(ordererTLSCert))); orderer_properties.setProperty("sslProvider", "openSSL"); orderer_properties.setProperty("negotiationType", "TLS"); Orderer orderer = client.newOrderer(ordererName, ordererURL,orderer_properties); channel.addOrderer(orderer); //init channel channel.initialize(); //query queryByChaincode(client,configurations,channel); //insert /*for (int i = 0; i <1; i++) { Business business = new Business(); business.setBizUUID("test"); business.setBizType("test"); business.setFillPerson("test"); business.setSubmissionTim("test"); business.setReviewer("test"); business.setReviewOpinion("test"); business.setReviewPass("test"); business.setToGzwTime("test"); business.setRequestTime(time.format(new Date())); business.setCount(i); business.setRequestID("test"); String json = JSON.toJSONString(business); insertBlockChain(client,configurations,channel,json); }*/ } /** * @description query * @params [client, configurations, channel] * @return void * @author adder * @date 2020/1/20 14:23 * */ public static void queryByChaincode(HFClient client,Configurations configurations,Channel channel) throws FileNotFoundException, ProposalException, InvalidArgumentException, UnsupportedEncodingException { String chaincodeName = configurations.loadConfigurations().getJsonObject("options").getString("chaincode_id"); ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build(); //build args ArrayList<String> argsList = new ArrayList<>(); argsList.add("test"); argsList.add("test"); //build query request QueryByChaincodeRequest request = client.newQueryProposalRequest(); request.setChaincodeID(chaincodeID); request.setFcn("query"); request.setArgs(argsList); Collection<ProposalResponse> responses = channel.queryByChaincode(request); ProposalResponse response = (ProposalResponse) responses.toArray()[0]; //analyse response if (response.getStatus().toString().equals("SUCCESS")){ System.out.println(response.getChaincodeActionResponseStatus()); String result = new String(response.getChaincodeActionResponsePayload(), StandardCharsets.UTF_8); System.out.println(result); JSONObject json = JSONObject.parseObject(result); String returnCode = (String) json.get("returnCode"); System.out.println(returnCode); } } /** * @description * @params [client, configurations, channel, message] * @return void * @author adder * @date 2020/1/21 15:42 * */ public static void insertBlockChain(HFClient client,Configurations configurations,Channel channel,String message) throws FileNotFoundException, InvalidArgumentException, ProposalException, ExecutionException, InterruptedException { String chaincodeName = configurations.loadConfigurations().getJsonObject("options").getString("chaincode_id"); ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build(); JSONObject msgJson = JSONObject.parseObject(message); msgJson.put("blockTimeTamp",System.currentTimeMillis()); //build args ArrayList<String> argsList = new ArrayList<>(); argsList.add(msgJson.toJSONString()); //build insert request TransactionProposalRequest request = client.newTransactionProposalRequest(); request.setChaincodeLanguage(TransactionRequest.Type.JAVA); request.setChaincodeID(chaincodeID); request.setArgs(argsList); request.setFcn("insert"); request.setProposalWaitTime(3000); Collection<ProposalResponse> responses = channel.sendTransactionProposal(request); for (ProposalResponse response : responses) { if (response.getChaincodeActionResponseStatus()==200){ System.out.println("Successfully sent Proposal and received ProposalResponse: status:"+response.getChaincodeActionResponseStatus()+",TransactionInformation: "+response.getProposalResponse().getResponse().getPayload().toStringUtf8()); } } CompletableFuture<TransactionEvent> transactionEvent = channel.sendTransaction(responses); TransactionEvent event = transactionEvent.get(); System.out.println("TransationID: "+event.getTransactionID()); if (event.isValid()){ System.out.println("Successfully committed the change to the ledger by the peer "+event.getPeer()); } } /** * @description get private key from key dir * @params [filePath] * @return java.io.File * @author adder * @date 2020/1/20 11:02 * */ private static File getKeyFilesInDir(File filePath) { File keyFile = null; File[] listFiles = filePath.listFiles(); if(listFiles != null) { for(File file:listFiles) { if(file.isFile()) { if(file.getName().endsWith("_sk")) { keyFile = file; break; } } } } return keyFile; } }
2.Business.class
package com.richfit.fabric.tls; import lombok.Data; /** * @author adder * @date 2020/1/20 15:43 */ @Data public class Business { public String getRequestID() { return requestID; } public void setRequestID(String requestID) { this.requestID = requestID; } private String requestID; private String bizUUID; private String bizType; private String fillPerson; private String submissionTim; private String reviewer; private String reviewOpinion; private String reviewPass; private String toGzwTime; private int count; private String requestTime; public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String getBizUUID() { return bizUUID; } public void setBizUUID(String bizUUID) { this.bizUUID = bizUUID; } public String getBizType() { return bizType; } public void setBizType(String bizType) { this.bizType = bizType; } public String getFillPerson() { return fillPerson; } public void setFillPerson(String fillPerson) { this.fillPerson = fillPerson; } public String getSubmissionTim() { return submissionTim; } public void setSubmissionTim(String submissionTim) { this.submissionTim = submissionTim; } public String getReviewer() { return reviewer; } public void setReviewer(String reviewer) { this.reviewer = reviewer; } public String getReviewOpinion() { return reviewOpinion; } public void setReviewOpinion(String reviewOpinion) { this.reviewOpinion = reviewOpinion; } public String getReviewPass() { return reviewPass; } public void setReviewPass(String reviewPass) { this.reviewPass = reviewPass; } public String getToGzwTime() { return toGzwTime; } public void setToGzwTime(String toGzwTime) { this.toGzwTime = toGzwTime; } public String getRequestTime() { return requestTime; } public void setRequestTime(String requestTime) { this.requestTime = requestTime; } }
3.Configurations.class
package com.richfit.fabric.tls; import org.yaml.snakeyaml.Yaml; import javax.json.Json; import javax.json.JsonObject; import java.io.*; import java.util.Map; /** * @author adder * @date 2020/1/20 15:43 */ public class Configurations { /** * @description * @params [] * @return javax.json.JsonObject * @author adder * @date 2020/1/24 13:03 * */ public JsonObject loadConfigurations() throws FileNotFoundException { String configPath = "src/main/java/com/richfit/fabric/tls/configure/configure.yml"; InputStream stream = new FileInputStream(new File(configPath)); Yaml yaml = new Yaml(); Map<String,Object> configYaml = yaml.load(stream); JsonObject configJSON = Json.createObjectBuilder(configYaml).build(); return configJSON; } }
4.FabricUser.class
package com.richfit.fabric.tls; 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; import java.nio.file.Files; import java.nio.file.Paths; import java.security.PrivateKey; import java.util.Set; /** * @author adder * @date 2020/1/20 15:43 */ public class FabricUser implements User { private String name; private String mspId; private Enrollment enrollment; private String keyFile; private String certFile; FabricUser(String name, String mspId, String keyFile, String certFile) { this.name = name; this.mspId = mspId; this.keyFile=keyFile; this.certFile=certFile; try{ enrollment=loadFromPemFile(keyFile, certFile); }catch(Exception ex){ ex.printStackTrace(); } } private Enrollment loadFromPemFile(String keyFile,String certFile) throws Exception{ byte[] keyPem = Files.readAllBytes(Paths.get(keyFile)); //load private key text byte[] certPem = Files.readAllBytes(Paths.get(certFile)); //load certificate text CryptoPrimitives suite = new CryptoPrimitives(); //load the cryptography suite PrivateKey privateKey = suite.bytesToPrivateKey(keyPem); //convert private key text to object return new X509Enrollment(privateKey,new String(certPem)); //create X509Enrollment object } @Override public String getName() { return name; } @Override public String getMspId() { return mspId; } @Override public Enrollment getEnrollment() { return enrollment; } @Override public String getAccount() { return null; } @Override public String getAffiliation() { return null; } @Override public Set<String> getRoles() { return null; } public String getKeyFile() { return keyFile; } public void setKeyFile(String keyFile) { this.keyFile = keyFile; } public String getCertFile() { return certFile; } public void setCertFile(String certFile) { this.certFile = certFile; } }
6.configure.yml
options: user_id: "Admin@org1.richfit.com" msp_id: "Org1MSP" channel_id: "mychannel" chaincode_id: "mycc" peer_url: "grpcs://peer0.org1.richfit.com:7051" orderer_url: "grpcs://orderer0.richfit.com:8050" privateKeyFolder: "src/main/java/com/richfit/fabric/tls/configure/crypto-config/peerOrganizations/org1.richfit.com/users/Admin@org1.richfit.com/msp/keystore" signedCert: "src/main/java/com/richfit/fabric/tls/configure/crypto-config/peerOrganizations/org1.richfit.com/users/Admin@org1.richfit.com/msp/signcerts/Admin@org1.richfit.com-cert.pem" peer_tls_cacerts: "src/main/java/com/richfit/fabric/tls/configure/crypto-config/peerOrganizations/org1.richfit.com/peers/peer0.org1.richfit.com/tls/ca.crt" orderer_tls_cacerts: "src/main/java/com/richfit/fabric/tls/configure/crypto-config/ordererOrganizations/richfit.com/orderers/orderer0.richfit.com/tls/ca.crt" peer_server_hostname: "peer0.org1.richfit.com" orderer_server_hostname: "orderer0.richfit.com"
来源:https://www.cnblogs.com/adderhuang/p/12234170.html