fabric java chaincode 开发
链码的开发不部分参考官网demo即可。
本文不会详细介绍开发过程
笔者启动的是一个gradle工程,也就是jar包管理使用的是gradle。
chaincode 源码:
/* Copyright IBM Corp., DTCC All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import com.alibaba.fastjson.JSONArray; import com.google.common.collect.Lists; import io.netty.handler.ssl.OpenSsl; import io.netty.util.internal.StringUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.example.bean.Factor; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import java.util.List; public class FactoringChaincode extends ChaincodeBase { private static Log _logger = LogFactory.getLog(FactoringChaincode.class); @Override public Response init(ChaincodeStub stub) { try { _logger.info("Init java factoring chaincode"); String func = stub.getFunction(); if (!func.equals("init")) { return newErrorResponse("function other than init is not supported"); } _logger.info("begin test keepAlive"); stub.putStringState("keepAliveTest","keepAliveTestValue"); _logger.info("end test keepAlive"); return newSuccessResponse(); } catch (Throwable e) { return newErrorResponse(e); } } @Override public Response invoke(ChaincodeStub stub) { try { _logger.info("Invoke java factoring chaincode"); String func = stub.getFunction(); List<String> params = stub.getParameters(); _logger.debug("Invoke function is "+stub.getFunction()+"Parameter is "+params.toString()); if(params.isEmpty() || params.size()<1 || params.get(0).length()==0){ return newErrorResponse("the invoke args not exist or arg[0] is emptyt"); } if (func.equals("SaveData")) { return saveData(stub, params); } if (func.equals("KeepaliveQuery")) { return keepaliveQuery(stub, params); } if (func.equals("QueryDataByFabricTxId")) { return queryDataByFabricTxId(stub, params); } if (func.equals("QueryDataByBusinessNo")) { return queryDataByBusinessNo(stub, params); } return newErrorResponse("Invalid invoke function name. "); } catch (Throwable e) { return newErrorResponse(e); } } private Response keepaliveQuery(ChaincodeStub stub, List<String> params) { String tarValue = stub.getStringState("keepAliveTest"); if(!"keepAliveTestValue".equals(tarValue)){ return newErrorResponse("ERROR! KeepaliveQuery get result is "+tarValue); } return newSuccessResponse("Reached".getBytes()); } private Response queryDataByBusinessNo(ChaincodeStub stub, List<String> params) { String txId =new String(stub.getState(params.get(0))); _logger.info("query data by businessNo:"+params.get(0)+",txId is"+txId); return this.queryDataByFabricTxId(stub, Lists.newArrayList(txId)); } private Response queryDataByFabricTxId(ChaincodeStub stub, List<String> params) { _logger.info("queryData by txId:"+params.get(0)); byte[] res = stub.getState(params.get(0)); return newSuccessResponse(new String(res),res); } private Response saveData(ChaincodeStub stub, List<String> args) { if(args.isEmpty() || args.size()<1){ return newSuccessResponse("saveData wrong args"); }try{ Factor f = JSONArray.toJavaObject(JSONArray.parseObject(args.get(0)),Factor.class); String businessNo = f.getBusinessNo(); if(StringUtil.isNullOrEmpty(businessNo)){ return newErrorResponse("businessNo must exist"); } String txId = stub.getTxId(); stub.putState(txId,args.get(0).getBytes()); stub.putState(businessNo,txId.getBytes()); _logger.info("save data successfully with txid "+txId+",and businessNo"+businessNo); }catch (Exception e){ return newErrorResponse(e.getMessage()); } return newSuccessResponse(); } public static void main(String[] args) { System.out.println("OpenSSL avaliable: " + OpenSsl.isAvailable()); new FactoringChaincode().start(args); } }
keepaliveQuery方法是为了探活。因为如果某个节点上没有启动链码容器会对交易的速度结果有影响,所以采用这种方式,每个节点都需要调用一次该方法在开始正常业务的调用。
链码的逻辑很简单,需要注意的是,gradle工程的配置文件中,需要制定baseName是 chaincode。因为在节点实例化链码的时候会默认找名称为chaincode的jar包。
shadowJar { baseName = 'chaincode' version = null classifier = null manifest { attributes 'Main-Class': 'org.hyperledger.fabric.example.FactoringChaincode' } }
mainfest中需要特别指定main方法的java 文件。否则在实例化的时候会报错。
需要注意的是,目前fabric应用的大部分地方场景中的部署环境是没有外网的。如银行,政府企业的环境。这个时候gradle就需要支持离线打包。
在build.gradle的配置文件中添加
dependencies { compile fileTree(dir: 'libs', includes: ['*.jar'])} 项目的根目录,跟Src同级建立一个libs文件夹,把需要的jar包放进去即可。
开发完成以后把编写的项目放到服务器上打包:
./peer chaincode package -n factor -l java -p /usr/local/workspace/java_chaincode/ -v 1.0 -s -S -i "AND ('Org1MSP.peer')" factor.out
-i "AND ('Org1MSP.peer')"表示该链码只能被Org1的peer节点实例化,其他节点实例化的话会报错。如果不指定这个参数,默认的限制是每个组织的admin可以实例化该链码。
打包的文件经过安装实例化就可以正常调用了。
附git地址 https://github.com/figo050518/fabric_java_chaincode