🔀 Hyperledger fabric java sdk assembly of public, core functional methods, providing further calls to other frameworks.
- v1.3 - 2018-12-20
- 增加私有数据功能支持
- 增加
META-INF
配置支持 - 增加
Java Chaincode
支持
- v1.1 - 2018-08-06
fabric sdk java commons framework
使用 fabric-sdk-java
的 1.3
版本。
在项目文件夹中添加目录 /fabric-sdk-commons/src/test/fixture/integration
,目录中增加资源配置。必须了解 end-to-end sample
实例的运行方式,这里不增加额外的赘述。
根据自己的需要可以适当的修改配置内容,这里的配置依赖上面配置资源/fabric-sdk-commons/src/test/fixture/integration
。配置文件位置 fabric-sdk-commons/src/test/resources/fabric-chaincode.properties
。 内容如下:
#@changelog properties file fabric configuration #Sat Jul 28 20:10:31 CST 2018 # 区块链网络版本 fabric.network.configtx.version=v1.0 # 用户 & 密码 hyperledger.fabric.sdk.commons.network.ca.admin.name=admin hyperledger.fabric.sdk.commons.network.ca.admin.passwd=adminpw hyperledger.fabric.sdk.commons.network.orgs.member.users=user1 hyperledger.fabric.sdk.commons.network.tls.enable=false # 公共的根目录 hyperledger.fabric.sdk.commons.config.root.path=src/test/fixture/integration # peer & order 证书配置 hyperledger.fabric.sdk.commons.crypto.channel.config.root.path=/e2e-2Orgs # channel & block 配置 hyperledger.fabric.sdk.commons.channel.artifacts.root.path=/channel-artifacts # Chaincode source code 目录 #hyperledger.fabric.sdk.commons.chaincode.source.code.root.path=/gocc/sample_11 hyperledger.fabric.sdk.commons.chaincode.source.code.root.path=/gocc/sample1 # 背书配置 hyperledger.fabric.sdk.commons.endorsement.policy.file.path=chaincode-endorsement-policy.yaml # 网络配置文件所在目录 hyperledger.fabric.sdk.commons.network.config.root.path=network_configs
持久化缓存配置文件 /fabric-sdk-commons/fabric-kv-store.properties
,存储通道或用户的缓存信息,方便下次直接使用而不用重复创建用户和通道。
fabric sdk java commons
框架支持自定义 FabricConfiguration
和 FabricKeyValueStore
的配置。如果需要自定义扩展配置,需要实现这两个特定的接口。然后在使用的时候传入自定义实现的实例即可。
public ChaincodeDeployTemplate(String channelName, String orgName, FabricConfiguration config, FabricKeyValueStore store) { super(channelName, orgName, config, store, ChaincodeDeployTemplate.class); this.init(); }
FabricConfiguration
支持两种网络链接配置方式,一种是 properties
配置文件的实现方式,参考代码:FabricPropertiesConfiguration.java
,另一种则是 class
内存的配置方式,参考代码:FabricClassConfiguration.java
。 可以在创建 ChaincodeDeployTemplate
或 ChaincodeTransactionTemplate
模板实例的时候,传入自己想要的配置模式。
-
properties config
配置的模板以下是
properties config
配置的模板,可以在模板的基础上修改配置定制
#@changelog properties file fabric configuration #Sat Jul 28 20:10:31 CST 2018 fabric.network.configtx.version=v1.1 hyperledger.fabric.sdk.commons.network.ca.admin.name=admin hyperledger.fabric.sdk.commons.network.ca.admin.passwd=adminpw hyperledger.fabric.sdk.commons.network.orgs.member.users=user1 hyperledger.fabric.sdk.commons.invoke.wait.time=10 hyperledger.fabric.sdk.commons.deploy.wait.time=100 hyperledger.fabric.sdk.commons.proposal.wait.time=1000 hyperledger.fabric.sdk.commons.network.tls.enable=false hyperledger.fabric.sdk.commons.network.domain=example.com hyperledger.fabric.sdk.commons.configtxlater.url=http\://192.168.8.8\:7059 hyperledger.fabric.sdk.commons.config.root.path=src/test/fixture/integration hyperledger.fabric.sdk.commons.crypto.channel.config.root.path=/e2e-2Orgs hyperledger.fabric.sdk.commons.channel.artifacts.root.path=/channel-artifacts hyperledger.fabric.sdk.commons.chaincode.source.code.root.path=/gocc/sample11 hyperledger.fabric.sdk.commons.endorsement.policy.file.path=chaincode-endorsement-policy.yaml hyperledger.fabric.sdk.commons.network.config.root.path=network_configs hyperledger.fabric.sdk.commons.network.org.peerOrg1.mspid=Org1MSP hyperledger.fabric.sdk.commons.network.org.peerOrg1.caName=ca0 hyperledger.fabric.sdk.commons.network.org.peerOrg1.domname=org1.example.com hyperledger.fabric.sdk.commons.network.org.peerOrg1.ca_location=http\://192.168.8.8\:7054 hyperledger.fabric.sdk.commons.network.org.peerOrg1.orderer_locations=orderer.example.com@grpc\://192.168.8.8\:7050 hyperledger.fabric.sdk.commons.network.org.peerOrg1.peer_locations=peer0.org1.example.com@grpc\://192.168.8.8\:7051, peer1.org1.example.com@grpc\://192.168.8.8\:7056 hyperledger.fabric.sdk.commons.network.org.peerOrg1.eventhub_locations=peer0.org1.example.com@grpc\://192.168.8.8\:7053, peer1.org1.example.com@grpc\://192.168.8.8\:7058 hyperledger.fabric.sdk.commons.network.org.peerOrg2.mspid=Org2MSP hyperledger.fabric.sdk.commons.network.org.peerOrg2.domname=org2.example.com hyperledger.fabric.sdk.commons.network.org.peerOrg2.ca_location=http\://192.168.8.8\:8054 hyperledger.fabric.sdk.commons.network.org.peerOrg2.orderer_locations=orderer.example.com@grpc\://192.168.8.8\:7050 hyperledger.fabric.sdk.commons.network.org.peerOrg2.peer_locations=peer0.org2.example.com@grpc\://192.168.8.8\:8051, peer1.org2.example.com@grpc\://192.168.8.8\:8056 hyperledger.fabric.sdk.commons.network.org.peerOrg2.eventhub_locations=peer0.org2.example.com@grpc\://192.168.8.8\:8053, peer1.org2.example.com@grpc\://192.168.8.8\:8058
-
class config
配置的模板以下是
class config
配置的模板,可以调用FabricClassConfiguration.getInstance()
修改配置定制
FabricClassConfiguration config = FabricClassConfiguration.getInstance(); String orgName = "peerOrg1"; config.setNetworkDomain("example.com"); config.setMspId(orgName, "Org1MSP"); config.setOrgDomain(orgName, "org1." + config.getNetworkDomain()); config.setCaName(orgName, "ca0"); config.setCaLocation(orgName, "http://" + config.getFabricNetworkHost() + ":7054"); config.setOrdererLocation(orgName, "orderer.example.com@grpc://" + config.getFabricNetworkHost() + ":7050"); config.setPeerLocation(orgName, "peer0.org1.example.com@grpc://" + config.getFabricNetworkHost() + ":7051, peer1.org1.example.com@grpc://" + config.getFabricNetworkHost() + ":7056"); config.setEventHubLocation(orgName, "peer0.org1.example.com@grpc://" + config.getFabricNetworkHost() + ":7053, peer1.org1.example.com@grpc://" + config.getFabricNetworkHost() + ":7058"); orgName = "peerOrg2"; config.setMspId(orgName, "Org2MSP"); config.setOrgDomain(orgName, "org2." + config.getNetworkDomain()); // config.setCaName(orgName, "ca1"); config.setCaLocation(orgName, "http://" + config.getFabricNetworkHost() + ":8054"); config.setOrdererLocation(orgName, "orderer.example.com@grpc://" + config.getFabricNetworkHost() + ":7050"); config.setPeerLocation(orgName, "peer0.org2.example.com@grpc://" + config.getFabricNetworkHost() + ":8051, peer1.org2.example.com@grpc://" + config.getFabricNetworkHost() + ":8056"); config.setEventHubLocation(orgName, "peer0.org2.example.com@grpc://" + config.getFabricNetworkHost() + ":8053, peer1.org2.example.com@grpc://" + config.getFabricNetworkHost() + ":8058"); config.settingPropertyValue(FabricConfigurationPropertyKey.FABRIC_CONFIGTX_VERSION, "v1.1"); config.settingPropertyValue(FabricConfigurationPropertyKey.CONFIG_TXLATER_URL, "http://" + config.getFabricNetworkHost() + ":7059"); config.settingPropertyValue(FabricConfigurationPropertyKey.INVOKE_WAIT_TIME, "10"); config.settingPropertyValue(FabricConfigurationPropertyKey.DEPLOY_WAIT_TIME, "100"); config.settingPropertyValue(FabricConfigurationPropertyKey.PROPOSAL_WAIT_TIME, "1000"); config.settingPropertyValue(FabricConfigurationPropertyKey.NETWORK_TLS_ENABLED, "true"); config.settingPropertyValue(FabricConfigurationPropertyKey.COMMON_CONFIG_ROOT_PATH, "src/test/fixture/integration"); config.settingPropertyValue(FabricConfigurationPropertyKey.CRYPTO_CHANNEL_CONFIG_ROOT_PATH, "/e2e-2Orgs"); config.settingPropertyValue(FabricConfigurationPropertyKey.CHAINCODE_SOURCE_ROOT_PATH, "/gocc/sample11"); config.settingPropertyValue(FabricConfigurationPropertyKey.CHANNEL_ARTIFACTS_ROOT_PATH, "/channel-artifacts"); config.settingPropertyValue(FabricConfigurationPropertyKey.ENDORSEMENT_POLICY_FILE_PATH, "chaincode-endorsement-policy.yaml"); config.settingPropertyValue(FabricConfigurationPropertyKey.NETWORK_CONFIG_ROOT_PATH, "network_configs"); config.settingPropertyValue(FabricConfigurationPropertyKey.NETWORK_CA_ADMIN_NAME, "admin"); config.settingPropertyValue(FabricConfigurationPropertyKey.NETWORK_CA_ADMIN_PASSWD, "adminpw"); config.settingPropertyValue(FabricConfigurationPropertyKey.NETWORK_ORGS_MEMBER_USERS, "user1");
FabricKeyValueStore
是KV持久化缓存配置,KV 也支持两种配置方式,一种是 FileSystemKeyValueStore
文件系统的配置方式,另一种是 MemoryKeyValueStore
内存系统的配置方式。如果有必要可以实现自己的持久化缓存存储,具体可以实现接口 io.github.hooj0.fabric.sdk.commons.store.FabricKeyValueStore
,同样可以在创建 ChaincodeDeployTemplate
或 ChaincodeTransactionTemplate
模板实例的时候,传入自己想要的配置模式。
公共的代码常量,通道和对等节点以及 ChaincodeId
public class BasedTemplateTest { final static String foo = "mychannel"; final static String bar = "mychannel2"; final static String org1 = "peerOrg1"; final static String org2 = "peerOrg2"; static String CHAIN_CODE_NAME = "example_cc_go"; static String CHAIN_CODE_PATH = "github.com/example_cc"; static String CHAIN_CODE_VERSION = "1"; static String CHAIN_CODE_VERSION_11 = "11"; static Type CHAIN_CODE_LANG = Type.GO_LANG; ChaincodeID chaincodeID_1 = ChaincodeID.newBuilder().setName(CHAIN_CODE_NAME).setPath(CHAIN_CODE_PATH).setVersion(CHAIN_CODE_VERSION).build(); ChaincodeID chaincodeID_11 = ChaincodeID.newBuilder().setName(CHAIN_CODE_NAME).setPath(CHAIN_CODE_PATH).setVersion(CHAIN_CODE_VERSION_11).build(); ChaincodeID chaincodeID_11_2 = ChaincodeID.newBuilder().setName(CHAIN_CODE_NAME).setPath(CHAIN_CODE_PATH).setVersion("11.2").build(); }
private ChaincodeDeployOperations operations; @Before public void setup() { operations = new ChaincodeDeployTemplate(foo, org1, FabricPropertiesConfiguration.getInstance()); // 自定义 key value store 文件位置 //operations = new ChaincodeDeployTemplate(foo, org1, FabricClassConfiguration.getInstance(), new File("my-kv-store.properties")); // 使用 内存 key value store 文件位置 //operations = new ChaincodeDeployTemplate(foo, org1, FabricPropertiesConfiguration.getInstance(), new MemoryKeyValueStore()); }
传入必要的参数 chaincodeID
和 Chaincode
的文件路径,以及 chaincode
的类型
@Test public void testInstallDeployTemplate() { InstallOptions options = new InstallOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); String chaincodeSourceFile = operations.getConfig().getChaincodeRootPath(); operations.install(options, chaincodeSourceFile); }
实例化支持多种结果返回形式,以及多种参数签名发送。
@Test public void testInstantiateDeployTemplate() { InstantiateOptions options = new InstantiateOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); ResultSet rs = operations.instantiate(options, "init", "a", 200, "b", 300); System.out.println("-------->>>>>>>" + rs); } @Test public void testInstantiate2DeployTemplate() { InstantiateOptions options = new InstantiateOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); LinkedHashMap<String, Object> args = Maps.newLinkedHashMap(); args.put("a", 300); args.put("b", 500); ResultSet rs = operations.instantiate(options, "init", args); System.out.println("-------->>>>>>>" + rs); } @Test public void testInstantiate3DeployTemplate() throws Exception { InstantiateOptions options = new InstantiateOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); LinkedHashMap<String, Object> args = Maps.newLinkedHashMap(); args.put("a", 300); args.put("b", 500); CompletableFuture<TransactionEvent> future = operations.instantiateAsync(options, "init", args); future.thenApply((BlockEvent.TransactionEvent transactionEvent) -> { // 必须是有效交易事件 checkArgument(transactionEvent.isValid(), "没有签名的交易事件"); // 必须有签名 checkNotNull(transactionEvent.getSignature(), "没有签名的交易事件"); // 必须有交易区块事件发生 BlockEvent blockEvent = checkNotNull(transactionEvent.getBlockEvent(), "交易事件的区块事件对象为空"); try { System.out.println("成功实例化Chaincode,本次实例化交易ID:" + transactionEvent.getTransactionID()); System.out.println(transactionEvent.getChannelId()); checkArgument(StringUtils.equals(blockEvent.getChannelId(), operations.getChannel().getName()), "事件名称和对应通道名称不一致"); // 检查 if (!operations.checkInstantiatedChaincode(options.getChaincodeId())) { throw new AssertionError("chaincode 1 没有实例化"); } } catch (Exception e) { throw new RuntimeException(e); } return "success"; }).exceptionally(e -> { if (e instanceof CompletionException && e.getCause() != null) { e = e.getCause(); } if (e instanceof TransactionEventException) { BlockEvent.TransactionEvent te = ((TransactionEventException) e).getTransactionEvent(); if (te != null) { e.printStackTrace(System.err); fail(format("Transaction with txid %s failed. %s", te.getTransactionID(), e.getMessage())); } } e.printStackTrace(System.err); fail(format("Test failed with %s exception %s", e.getClass().getName(), e.getMessage())); return null; }).get(operations.getConfig().getTransactionWaitTime(), TimeUnit.SECONDS); } @Test public void testInstantiate4DeployTemplate() { InstantiateOptions options = new InstantiateOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); LinkedHashMap<String, Object> args = Maps.newLinkedHashMap(); args.put("a", 300); args.put("b", 500); TransactionEvent event = operations.instantiateFor(options, "init", args); System.out.println("-------------->>>>>>>" + event); }
升级后,每笔交易会发生在两个链码上。
@Test public void testUpgradeDeployTemplate() throws Exception { if (!operations.checkChaincode(chaincodeID_11, operations.getOrganization())) { fail(chaincodeID_11 + " not install | instantiate."); } if (!operations.checkInstallChaincode(chaincodeID_11_2)) { InstallOptions options = new InstallOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setChaincodeUpgradeVersion("11.2"); operations.install(options, operations.getConfig().getChaincodeRootPath()); } if (operations.checkInstallChaincode(chaincodeID_11_2) && !operations.checkInstantiatedChaincode(chaincodeID_11_2)) { UpgradeOptions upgradeOptions = new UpgradeOptions(); upgradeOptions.setChaincodeId(chaincodeID_11_2).setChaincodeType(CHAIN_CODE_LANG); upgradeOptions.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); ResultSet rs = operations.upgrade(upgradeOptions, "init"); System.out.println("-------------->>>>>>>" + rs); } } @Test public void testUpgrade2DeployTemplate() throws Exception { UpgradeOptions options = new UpgradeOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG).setChaincodeVersion("11.2"); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); InstallOptions installOptions = new InstallOptions().setChaincodeUpgradeVersion("11.2"); installOptions.setChaincodeId(options.getChaincodeId()).setChaincodeType(CHAIN_CODE_LANG); String chaincodeSourceFile = operations.getConfig().getChaincodeRootPath(); if (!operations.checkInstallChaincode(installOptions.getChaincodeId())) { operations.install(installOptions, chaincodeSourceFile); } CompletableFuture<TransactionEvent> future = operations.upgradeAsync(options, "init", "a", "100", "b", "100"); Object result = future.thenApply((TransactionEvent event) -> { System.out.println(event.isValid()); System.out.println(event.getBlockEvent()); System.out.println(event.getChannelId()); System.out.println(event.getTimestamp()); System.out.println(event.getSignature()); System.out.println(event.getValidationCode()); System.out.println(event.getType()); return event.getTransactionID(); }).exceptionally(e -> { e.printStackTrace(); return null; }).get(10000, TimeUnit.MILLISECONDS); System.out.println("-------------->>>>>>>" + result); } @Test public void testUpgrade3DeployTemplate() throws Exception { UpgradeOptions options = new UpgradeOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG).setChaincodeVersion("11.2"); options.setEndorsementPolicyFile(Paths.get(operations.getConfig().getEndorsementPolicyFilePath()).toFile()); InstallOptions installOptions = new InstallOptions().setChaincodeUpgradeVersion("11.2"); installOptions.setChaincodeId(options.getChaincodeId()).setChaincodeType(CHAIN_CODE_LANG); String chaincodeSourceFile = operations.getConfig().getChaincodeRootPath(); if (!operations.checkInstallChaincode(installOptions.getChaincodeId())) { operations.install(installOptions, chaincodeSourceFile); } TransactionEvent event = operations.upgradeFor(options, "init"); System.out.println("-------------->>>>>>>" + event); }
交易是最常用的业务接口调用方式,一般交易分为查询交易和修改交易请求两种。
同样可以支持自定义传入 config
和 store
的实现
private ChaincodeTransactionOperations operations; @Before public void setup() { operations = new ChaincodeTransactionTemplate(foo, org1, FabricPropertiesConfiguration.getInstance()); //operations = new ChaincodeTransactionTemplate(foo, org1, FabricPropertiesConfiguration.getInstance(), new File("my-kv-store.properties")); //operations = new ChaincodeTransactionTemplate(foo, org1, FabricPropertiesConfiguration.getInstance(), new MemoryKeyValueStore()); }
执行交易执行3种不同方式的结果返回形式,普通的结果封装 ResultSet
和 异步线程模型结果 CompletableFuture<TransactionEvent>
以及 TransactionEvent
结果返回,适用于不同的业务场景需求。
@Test public void testInvokeTransactionTemplate() { InvokeOptions options = new InvokeOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setClientUserContext(operations.getOrganization().getUser("user1")); ResultSet rs = operations.invoke(options, "move", "a", "b", 20); System.out.println(rs); try { TransactionEvent event = operations.getChannel().sendTransaction(rs.getResponses()).get(); System.out.println(event.isValid()); System.out.println(event.getBlockEvent()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } @Test public void testInvokeTransactionTemplate2() throws InterruptedException, ExecutionException, TimeoutException { InvokeOptions options = new InvokeOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setClientUserContext(operations.getOrganization().getUser("user1")); CompletableFuture<TransactionEvent> future = operations.invokeAsync(options, "move", "b", "a", 3); future.thenApply((BlockEvent.TransactionEvent transactionEvent) -> { // 必须是有效交易事件 checkArgument(transactionEvent.isValid(), "没有签名的交易事件"); // 必须有签名 checkNotNull(transactionEvent.getSignature(), "没有签名的交易事件"); // 必须有交易区块事件发生 BlockEvent blockEvent = checkNotNull(transactionEvent.getBlockEvent(), "交易事件的区块事件对象为空"); try { System.out.println("成功实例化Chaincode,本次实例化交易ID:" + transactionEvent.getTransactionID()); System.out.println(transactionEvent.getChannelId()); checkArgument(StringUtils.equals(blockEvent.getChannelId(), operations.getChannel().getName()), "事件名称和对应通道名称不一致"); } catch (Exception e) { throw new RuntimeException(e); } QueryOptions options2 = new QueryOptions(); options2.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options2.setProposalWaitTime(100000); options2.setSpecificPeers(false); //operations.instantiate("hoojo3"); options2.setClientUserContext(operations.getOrganization().getUser("hoojo5")); String a = operations.query(options2, "query", "a"); String b = operations.query(options2, "query", "b"); System.out.println("a mount ---->" + a); System.out.println("b mount ---->" + b); return "success"; }).exceptionally(e -> { if (e instanceof CompletionException && e.getCause() != null) { e = e.getCause(); } if (e instanceof TransactionEventException) { BlockEvent.TransactionEvent te = ((TransactionEventException) e).getTransactionEvent(); if (te != null) { e.printStackTrace(System.err); fail(format("Transaction with txid %s failed. %s", te.getTransactionID(), e.getMessage())); } } e.printStackTrace(System.err); fail(format("Test failed with %s exception %s", e.getClass().getName(), e.getMessage())); return null; }).get(operations.getConfig().getTransactionWaitTime(), TimeUnit.SECONDS); } @Test public void testInvokeTransactionTemplate3() { InvokeOptions options = new InvokeOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setClientUserContext(operations.getOrganization().getUser("user1")); TransactionEvent event = operations.invokeFor(options, "move", "a", "b", 5); System.out.println(event.getTransactionID()); System.out.println(event.getCreator().getId()); }
查询支持两种结果返回的方式,返回字符串和封装的结果集 ResultSet
。
@Test public void testQueryTransactionTemplate() { QueryOptions options = new QueryOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); options.setProposalWaitTime(100000); options.setSpecificPeers(false); operations.instantiate("hoojo3"); options.setClientUserContext(operations.getOrganization().getUser("hoojo3")); // options.setRequestUser(requestUser); String rs = operations.query(options, "query", "a"); System.out.println(rs); rs = operations.query(options, "query", "b"); System.out.println(rs); } @Test public void testQueryTransactionTemplate2() { QueryOptions options = new QueryOptions(); options.setChaincodeId(chaincodeID_11).setChaincodeType(CHAIN_CODE_LANG); operations.instantiate("hoojo3"); options.setClientUserContext(operations.getOrganization().getUser("user1")); options.setRequestUser(operations.getOrganization().getUser("hoojo3")); ResultSet rs = operations.queryFor(options, "query", "a"); System.out.println(rs); }
如果有任何疑问或交流的想法,欢迎在 issues 发布话题,我会及时跟进。