借鑑網上的一篇博文,實現向fabric區塊鏈上存入數據摘要並查詢最新的數據記錄。java
使用的fabric1.4.1單機單節點網絡,採用solo共識(多機kafka共識環境也可以使用);採用docker部署;關閉TLS;chaincode採用Java編寫;fabric狀態數據庫爲couchdb使用了數據庫索引;fabric-sdk-java依賴版本爲1.4.1。sql
建議有必定fabric基礎的同窗食用。docker
筆者使用的fabric的單機單節點網絡實例://download.csdn.net/download/weixin_43562234/12116307數據庫
博客園不支持資源上傳,很是抱歉~~~~~apache
代碼部分:
代碼結構:
代碼正文
1.App.classjson
package com.richfit.fabric; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import com.alibaba.fastjson.JSONObject; import org.hyperledger.fabric.sdk.*; import org.hyperledger.fabric.sdk.BlockEvent.TransactionEvent; import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; import org.hyperledger.fabric.sdk.exception.ProposalException; import org.hyperledger.fabric.sdk.security.CryptoSuite; /** * Hello world! */ public class App { public static void main(String[] args) throws Exception { Configurations configurations = new Configurations(); HFClient client = HFClient.createNewInstance(); Channel channel = initChannel(client,configurations); //query queryByChaincode(client,configurations,channel); //insert /*for (int i = 0; i <3; 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("test"); business.setCount(i); business.setRequestID("test"); //Thread.sleep(1000); System.out.println("messageCount: "+i); 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); } } 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"); Collection<ProposalResponse> responses = channel.sendTransactionProposal(request); CompletableFuture<TransactionEvent> transactionEvent = channel.sendTransaction(responses); TransactionEvent event = transactionEvent.get(); System.out.println(event.getTransactionID()); System.out.println(event.isValid()); } /** * @description * @params [client, configurations] * @return org.hyperledger.fabric.sdk.Channel * @author adder * @date 2020/1/20 14:27 * */ private static Channel initChannel(HFClient client,Configurations configurations) throws Exception { //create user object 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"); FabricUser user = new FabricUser(userName, mspId, keyFile, certFile); client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite()); client.setUserContext(user); //create channel object Channel channel = client.newChannel(channelName); //create peer Peer peer = client.newPeer(peerName, peerURL); channel.addPeer(peer); //orderer Orderer orderer = client.newOrderer(ordererName, ordererURL); channel.addOrderer(orderer); channel.initialize(); return channel; } /** * @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; import lombok.Data; @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.classdom
package com.richfit.fabric; import org.yaml.snakeyaml.Yaml; import javax.json.Json; import javax.json.JsonObject; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Map; public class Configurations { public JsonObject loadConfigurations() throws FileNotFoundException { String configPath = "src/main/java/com/richfit/fabric/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.classide
package com.richfit.fabric; 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 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; } }
5.configure.yml區塊鏈
options: user_id: "Admin@org1.gzjg.com" msp_id: "Org1MSP" channel_id: "mychannel" chaincode_id: "mycc" peer_url: "grpc://192.168.43.66:7051" orderer_url: "grpc://192.168.43.66:7050" privateKeyFolder: "src/main/java/com/richfit/fabric/configure/crypto-config/peerOrganizations/org1.gzjg.com/users/Admin@org1.gzjg.com/msp/keystore/" signedCert: "src/main/java/com/richfit/fabric/configure/crypto-config/peerOrganizations/org1.gzjg.com/users/Admin@org1.gzjg.com/msp/signcerts/Admin@org1.gzjg.com-cert.pem" peer_tls_cacerts: "src/main/java/com/richfit/fabric/configure/crypto-config/peerOrganizations/org1.gzjg.com/peers/peer0.org1.gzjg.com/tls/ca.crt" orderer_tls_cacerts: "src/main/java/com/richfit/fabric/configure/crypto-config/ordererOrganizations/gzjg.com/orderers/orderer.gzjg.com/tls/ca.crt" peer_server_hostname: "peer0.org1.gzjg.com" orderer_server_hostname: "orderer.gzjg.com"
6.fabric-chaincode-java
package org.hyperledger.fabric.example; import java.io.UnsupportedEncodingException; import java.util.*; import com.alibaba.fastjson.JSON; import com.google.protobuf.ByteString; import io.netty.handler.ssl.OpenSsl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; import static java.nio.charset.StandardCharsets.UTF_8; public class SimpleChaincode extends ChaincodeBase { private static Log _logger = LogFactory.getLog(SimpleChaincode.class); @Override public Response init(ChaincodeStub stub) { try { _logger.info("Init java simpleS chaincode"); String func = stub.getFunction(); if (!func.equals("init")) { return newErrorResponse("function other than init is not supported"); } List<String> args = stub.getParameters(); String account1Key = args.get(0); System.out.println(account1Key); String account1Value = args.get(1); System.out.println(account1Value); _logger.info(String.format("account %s, value = %s", account1Key, account1Value)); return newSuccessResponse(); } catch (Throwable e) { return newErrorResponse(e); } } @Override public Response invoke(ChaincodeStub stub) { try { _logger.info("Invoke java simple chaincode"); String func = stub.getFunction(); List<String> params = stub.getParameters(); if (func.equals("invoke")) { return invoke(stub, params); } if (func.equals("delete")) { return delete(stub, params); } if (func.equals("query")) { return query(stub, params); } if (func.equals("insert")) { return insert(stub, params); } return newErrorResponse( "Invalid invoke function name. Expecting one of: [\"invoke\", \"delete\", \"query\"]"); } catch (Throwable e) { return newErrorResponse(e); } } private Response insert(ChaincodeStub stub, List<String> args) throws UnsupportedEncodingException { String msgkey = UUID.randomUUID().toString(); // String msgKey =args.get(0); String msgValue = args.get(0); stub.putState(msgkey, msgValue.getBytes(UTF_8)); return newSuccessResponse("insert finished successfully", ByteString.copyFrom(msgkey + ": " + msgValue, UTF_8).toByteArray()); } private Response invoke(ChaincodeStub stub, List<String> args) { String msgKey = args.get(0); String msgValue = args.get(1); stub.putStringState(msgKey, msgValue); return newSuccessResponse("invoke finished successfully", ByteString.copyFrom(msgKey + ": " + msgValue, UTF_8).toByteArray()); } // Deletes an entity from state private Response delete(ChaincodeStub stub, List<String> args) { if (args.size() != 1) { return newErrorResponse("Incorrect number of arguments. Expecting 1"); } String key = args.get(0); // Delete the key from the state in ledger stub.delState(key); return newSuccessResponse(); } /* * query callback representing the query of a chaincode rich query */ private Response query(ChaincodeStub stub, List<String> args) { System.out.println("agrs: "+args); try { long start = System.currentTimeMillis(); if (args.size() != 2) { return newErrorResponse( "Incorrect number of arguments. Expecting name of the person to query"); } // SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); String args1 = args.get(0); String args2 = args.get(1); // test String sql = String.format( "{\"selector\":{\"bizUUID\": \"%s\",\"bizType\":\"%s\"},\"sort\":[{\"blockTimeTamp\":\"desc\"}]}", args.get(0), args.get(1)); _logger.info("sql: " + sql); // QueryResultsIterator<KeyValue> dataResult = stub.getQueryResult(sql); QueryResultsIteratorWithMetadata<KeyValue> dataResult = stub.getQueryResultWithPagination(sql, 50, ""); _logger.info("query 1: " + (System.currentTimeMillis() - start)); System.out.println("dataResult.iterator().hasNext(): " + dataResult.iterator().hasNext()); if (dataResult.iterator().hasNext()) { List<String> dataList = new ArrayList(); while (dataResult.iterator().hasNext()) { KeyValue dr = dataResult.iterator().next(); String drKey = dr.getKey(); String drValue = dr.getStringValue(); System.out.println("drKey: " + drKey + " \n" + "drValue: " + drValue); dataList.add(drValue); } _logger.info("query 2: " + (System.currentTimeMillis() - start)); _logger.info("dataList.size(): " + dataList.size()); _logger.info("query 3: " + (System.currentTimeMillis() - start)); _logger.info("dataList: " + dataList.get(0)); Map<String, String> resultMap = new HashMap<>(); resultMap.put("returnCode", "success"); resultMap.put("result", dataList.get(0)); String jsonString = JSON.toJSONString(resultMap); _logger.info(String.format("Query Response:\n %s", jsonString)); return newSuccessResponse(String.valueOf(jsonString), ByteString.copyFrom(String.valueOf(jsonString), UTF_8).toByteArray()); } else { String result = String.format("State for [bizUUID= %s , bizType= %s] is null", args1, args2); Map<String, String> resulMap = new HashMap<>(); resulMap.put("returnCode", "no data"); resulMap.put("result", result); String jsonString = JSON.toJSONString(resulMap); return newSuccessResponse(String.valueOf(jsonString), ByteString.copyFrom(String.valueOf(jsonString), UTF_8).toByteArray()); } } catch (Exception e) { return newErrorResponse("query error : " + e); } } public static void main(String[] args) { System.out.println("OpenSSL avaliable: " + OpenSsl.isAvailable()); new SimpleChaincode().start(args); } }