Skip to content

Commit 99b6817

Browse files
author
Luis Sanchez
committedApr 26, 2017
[FAB-3170] invokechaincode from java chaincode
- Added invokeChaincode methods to ChaincodeStub - Added Java chaincode test to existing TestChaincodeInvokeChaincode Change-Id: I1b0674f6cc40cf6c3c5b10fb8d24274190e115d6 Signed-off-by: Luis Sanchez <sanchezl@us.ibm.com>
1 parent 852997a commit 99b6817

File tree

11 files changed

+820
-269
lines changed

11 files changed

+820
-269
lines changed
 

‎core/chaincode/chaincodetest.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ logging:
134134
# info - Set default to INFO
135135
# warning:main,db=debug:chaincode=info - Override default WARNING in main,db,chaincode
136136
# chaincode=info:main=debug:db=debug:warning - Same as above
137-
peer: debug
138-
crypto: info
137+
peer: warning
138+
crypto: warning
139139
status: warning
140140
stop: warning
141141
login: warning
142-
vm: debug
143-
chaincode: debug
142+
vm: warning
143+
chaincode: warning
144144

145145

146146
###############################################################################

‎core/chaincode/exectransaction_test.go

+168-146
Large diffs are not rendered by default.

‎core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java

+73-20
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package org.hyperledger.fabric.shim;
1818

19+
import java.nio.charset.StandardCharsets;
20+
import java.util.Arrays;
1921
import java.util.Collections;
2022
import java.util.List;
2123
import java.util.stream.Collectors;
2224

2325
import org.apache.commons.logging.Log;
2426
import org.apache.commons.logging.LogFactory;
2527
import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent;
28+
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
2629

2730
import com.google.protobuf.ByteString;
2831

@@ -195,13 +198,78 @@ public String createCompositeKey(String objectType, String[] attributes) {
195198
}
196199

197200
/**
198-
* @param chaincodeName
199-
* @param function
200-
* @param args
201+
* Invoke another chaincode using the same transaction context.
202+
*
203+
* @param chaincodeName Name of chaincode to be invoked.
204+
* @param args Arguments to pass on to the called chaincode.
205+
* @param channel If not specified, the caller's channel is assumed.
206+
* @return
207+
*/
208+
public Response invokeChaincode(final String chaincodeName, final List<byte[]> args, final String channel) {
209+
// internally we handle chaincode name as a composite name
210+
final String compositeName;
211+
if(channel != null && channel.trim().length() > 0) {
212+
compositeName = chaincodeName + "/" + channel;
213+
} else {
214+
compositeName = chaincodeName;
215+
}
216+
return handler.handleInvokeChaincode(compositeName, args, this.uuid);
217+
}
218+
219+
/**
220+
* Invoke another chaincode using the same transaction context.
221+
*
222+
* @param chaincodeName Name of chaincode to be invoked.
223+
* @param args Arguments to pass on to the called chaincode.
224+
* @return
225+
*/
226+
public Response invokeChaincode(final String chaincodeName, final List<byte[]> args) {
227+
return invokeChaincode(chaincodeName, args, null);
228+
}
229+
230+
/**
231+
* Invoke another chaincode using the same transaction context.
232+
*
233+
* This is a convenience version of {@link #invokeChaincode(String, List, String)}.
234+
* The string args will be encoded into as UTF-8 bytes.
235+
*
236+
* @param chaincodeName Name of chaincode to be invoked.
237+
* @param args Arguments to pass on to the called chaincode.
238+
* @param channel If not specified, the caller's channel is assumed.
239+
* @return
240+
*/
241+
public Response invokeChaincodeWithStringArgs(final String chaincodeName, final List<String> args, final String channel) {
242+
return invokeChaincode(chaincodeName, args.stream().map(x->x.getBytes(StandardCharsets.UTF_8)).collect(Collectors.toList()), channel);
243+
}
244+
245+
/**
246+
* Invoke another chaincode using the same transaction context.
247+
*
248+
* This is a convenience version of {@link #invokeChaincode(String, List)}.
249+
* The string args will be encoded into as UTF-8 bytes.
250+
*
251+
*
252+
* @param chaincodeName Name of chaincode to be invoked.
253+
* @param args Arguments to pass on to the called chaincode.
201254
* @return
202255
*/
203-
public String invokeChaincode(String chaincodeName, String function, List<ByteString> args) {
204-
return handler.handleInvokeChaincode(chaincodeName, function, args, uuid).toStringUtf8();
256+
public Response invokeChaincodeWithStringArgs(final String chaincodeName, final List<String> args) {
257+
return invokeChaincodeWithStringArgs(chaincodeName, args, null);
258+
}
259+
260+
/**
261+
* Invoke another chaincode using the same transaction context.
262+
*
263+
* This is a convenience version of {@link #invokeChaincode(String, List)}.
264+
* The string args will be encoded into as UTF-8 bytes.
265+
*
266+
*
267+
* @param chaincodeName Name of chaincode to be invoked.
268+
* @param args Arguments to pass on to the called chaincode.
269+
* @return
270+
*/
271+
public Response invokeChaincodeWithStringArgs(final String chaincodeName, final String... args) {
272+
return invokeChaincodeWithStringArgs(chaincodeName, Arrays.asList(args), null);
205273
}
206274

207275
// ------RAW CALLS------
@@ -233,19 +301,4 @@ public void putRawState(String key, ByteString value) {
233301
// return handler.handleGetStateByRange(startKey, endKey, limit, uuid);
234302
// }
235303

236-
/**
237-
* Invokes the provided chaincode with the given function and arguments, and
238-
* returns the raw ByteString value that invocation generated.
239-
*
240-
* @param chaincodeName
241-
* The name of the chaincode to invoke
242-
* @param function
243-
* the function parameter to pass to the chaincode
244-
* @param args
245-
* the arguments to be provided in the chaincode call
246-
* @return the value returned by the chaincode call
247-
*/
248-
public ByteString invokeRawChaincode(String chaincodeName, String function, List<ByteString> args) {
249-
return handler.handleInvokeChaincode(chaincodeName, function, args, uuid);
250-
}
251304
}

‎core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/Handler.java

+50-63
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.HashMap;
3636
import java.util.List;
3737
import java.util.Map;
38+
import java.util.stream.Collectors;
3839

3940
import org.apache.commons.logging.Log;
4041
import org.apache.commons.logging.LogFactory;
@@ -635,78 +636,64 @@ public QueryStateResponse handleGetStateByRange(String startKey, String endKey,
635636
}
636637
}
637638
*/
638-
public ByteString handleInvokeChaincode(String chaincodeName, String function, List<ByteString> args, String uuid) {
639-
// Check if this is a transaction
640-
if (!isTransaction.containsKey(uuid)) {
641-
throw new RuntimeException("Cannot invoke chaincode in query context");
642-
}
643-
644-
ChaincodeID id = ChaincodeID.newBuilder()
645-
.setName(chaincodeName).build();
646-
ChaincodeInput input = ChaincodeInput.newBuilder()
647-
.addArgs(ByteString.copyFromUtf8(function))
648-
.addAllArgs(args)
649-
.build();
650-
ChaincodeSpec payload = ChaincodeSpec.newBuilder()
651-
.setChaincodeId(id)
652-
.setInput(input)
639+
public Response handleInvokeChaincode(String chaincodeName, List<byte[]> args, String txid) {
640+
641+
// create invocation specification of the chaincode to invoke
642+
final ChaincodeSpec invocationSpec = ChaincodeSpec.newBuilder()
643+
.setChaincodeId(ChaincodeID.newBuilder()
644+
.setName(chaincodeName)
645+
.build())
646+
.setInput(ChaincodeInput.newBuilder()
647+
.addAllArgs(args.stream().map(ByteString::copyFrom).collect(Collectors.toList()))
648+
.build())
653649
.build();
654650

655-
// Create the channel on which to communicate the response from validating peer
656-
Channel<ChaincodeMessage> responseChannel;
657-
try {
658-
responseChannel = createChannel(uuid);
659-
} catch (Exception e) {
660-
logger.error(String.format("[%s]Another state request pending for this Uuid. Cannot process.", shortID(uuid)));
661-
throw e;
662-
}
663-
664-
//Defer
665651
try {
652+
// create the channel on which to communicate the response from validating peer
653+
final Channel<ChaincodeMessage> responseChannel = createChannel(txid);
654+
666655
// Send INVOKE_CHAINCODE message to validator chaincode support
667-
ChaincodeMessage message = ChaincodeMessage.newBuilder()
668-
.setType(INVOKE_CHAINCODE)
669-
.setPayload(payload.toByteString())
670-
.setTxid(uuid)
671-
.build();
672-
673-
logger.debug(String.format("[%s]Sending %s",
674-
shortID(message), INVOKE_CHAINCODE));
675-
676-
try {
677-
serialSend(message);
678-
} catch (Exception e) {
679-
logger.error("["+ shortID(message)+"]Error sending "+INVOKE_CHAINCODE+": "+e.getMessage());
680-
throw e;
681-
}
682-
683-
// Wait on responseChannel for response
684-
ChaincodeMessage response;
685-
try {
686-
response = receiveChannel(responseChannel);
687-
} catch (Exception e) {
688-
logger.error(String.format("[%s]Received unexpected message type", shortID(message)));
689-
throw new RuntimeException("Received unexpected message type");
690-
}
656+
final ChaincodeMessage message = HandlerHelper.newInvokeChaincodeMessage(txid, invocationSpec.toByteString());
657+
logger.debug(String.format("[%s]Sending %s", shortID(message), INVOKE_CHAINCODE));
658+
serialSend(message);
691659

692-
if (response.getType() == RESPONSE) {
693-
// Success response
694-
logger.debug(String.format("[%s]Received %s. Successfully invoked chaincode", shortID(response.getTxid()), RESPONSE));
695-
return response.getPayload();
660+
// wait for response chaincode message
661+
final ChaincodeMessage outerResponseMessage = receiveChannel(responseChannel);
662+
663+
if(outerResponseMessage == null) {
664+
return ChaincodeHelper.newInternalServerErrorResponse("chaincode invoke returned null");
696665
}
697-
698-
if (response.getType() == ERROR) {
666+
667+
logger.debug(String.format("[%s]Received %s.", shortID(outerResponseMessage.getTxid()), outerResponseMessage.getType()));
668+
669+
switch (outerResponseMessage.getType()) {
670+
case RESPONSE:
671+
// response message payload should be yet another chaincode message (the actual response message)
672+
final ChaincodeMessage responseMessage = ChaincodeMessage.parseFrom(outerResponseMessage.getPayload());
673+
// the actual response message must be of type COMPLETED
674+
logger.debug(String.format("[%s]Received %s.", shortID(responseMessage.getTxid()), responseMessage.getType()));
675+
if(responseMessage.getType() == COMPLETED) {
676+
// success
677+
return Response.parseFrom(responseMessage.getPayload());
678+
} else {
679+
// error
680+
return ChaincodeHelper.newInternalServerErrorResponse(responseMessage.getPayload().toByteArray());
681+
}
682+
case ERROR:
699683
// Error response
700-
logger.error(String.format("[%s]Received %s.", shortID(response.getTxid()), ERROR));
701-
throw new RuntimeException(response.getPayload().toStringUtf8());
684+
logger.error(String.format("[%s]Received %s.", shortID(outerResponseMessage.getTxid()), ERROR));
685+
return ChaincodeHelper.newInternalServerErrorResponse(outerResponseMessage.getPayload().toByteArray());
686+
default:
687+
// Incorrect chaincode message received
688+
logger.debug(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",shortID(outerResponseMessage.getTxid()), outerResponseMessage.getType(), RESPONSE, ERROR));
689+
return ChaincodeHelper.newInternalServerErrorResponse("Incorrect chaincode message received.", outerResponseMessage.toByteArray());
702690
}
703-
704-
// Incorrect chaincode message received
705-
logger.debug(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",
706-
shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));
707-
throw new RuntimeException("Incorrect chaincode message received");
691+
} catch (InvalidProtocolBufferException e) {
692+
return ChaincodeHelper.newInternalServerErrorResponse(e);
693+
} catch (RuntimeException e) {
694+
return ChaincodeHelper.newInternalServerErrorResponse(e);
708695
} finally {
709-
deleteChannel(uuid);
696+
deleteChannel(txid);
710697
}
711698
}
712699

‎core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/HandlerHelper.java

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.COMPLETED;
1717
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.ERROR;
1818
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE;
19+
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.INVOKE_CHAINCODE;
1920

2021
import java.io.PrintWriter;
2122
import java.io.StringWriter;
@@ -48,6 +49,10 @@ static ChaincodeMessage newErrorEventMessage(final String txid, final String mes
4849
static ChaincodeMessage newCompletedEventMessage(final String txid, final Response response, final ChaincodeEvent event) {
4950
return newEventMessage(COMPLETED, txid, response.toByteString(), event);
5051
}
52+
53+
static ChaincodeMessage newInvokeChaincodeMessage(final String txid, final ByteString payload) {
54+
return newEventMessage(INVOKE_CHAINCODE, txid, payload, null);
55+
}
5156

5257
private static ChaincodeMessage newEventMessage(final Type type, final String txid, final ByteString payload) {
5358
return newEventMessage(type, txid, payload, null);

‎examples/chaincode/java/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/.classpath
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright DTCC 2016 All Rights Reserved.
2+
Copyright DTCC, IBM 2016, 2017 All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -16,59 +16,70 @@
1616

1717
package example;
1818

19-
import com.google.protobuf.ByteString;
20-
import org.hyperledger.fabric.shim.ChaincodeBase;
21-
import org.hyperledger.fabric.shim.ChaincodeStub;
19+
import static java.lang.String.format;
20+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newBadRequestResponse;
21+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newInternalServerErrorResponse;
22+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newSuccessResponse;
2223

23-
import java.nio.charset.StandardCharsets;
24-
import java.util.LinkedList;
2524
import java.util.List;
2625

26+
import org.hyperledger.fabric.protos.common.Common.Status;
27+
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
28+
import org.hyperledger.fabric.shim.ChaincodeBase;
29+
import org.hyperledger.fabric.shim.ChaincodeStub;
30+
2731
public class LinkExample extends ChaincodeBase {
2832

29-
//Default name for map chaincode in dev mode
30-
//Can be set to a hash location via init or setMap
31-
private String mapChaincode = "map";
33+
/** Default name for map chaincode in dev mode. Can be set to a hash location via init or setMap */
34+
private static final String DEFAULT_MAP_CHAINCODE_NAME = "map";
35+
36+
private String mapChaincodeName = DEFAULT_MAP_CHAINCODE_NAME;
3237

3338
@Override
34-
public String run(ChaincodeStub stub, String function, String[] args) {
35-
switch (function) {
36-
case "init":
37-
case "setMap":
38-
mapChaincode = args[0];
39-
break;
40-
case "put":
41-
stub.invokeChaincode(mapChaincode, function, toByteStringList(args), "");
42-
default:
43-
break;
44-
}
45-
return null;
39+
public Response init(ChaincodeStub stub) {
40+
return invoke(stub);
4641
}
47-
42+
4843
@Override
49-
public String query(ChaincodeStub stub, String function, String[] args) {
50-
String tmp = stub.queryChaincode("map", function, toByteStringList(args));
51-
if (tmp.isEmpty()) tmp = "NULL";
52-
else tmp = "\"" + tmp + "\"";
53-
tmp += " (queried from map chaincode)";
54-
return tmp;
44+
public Response invoke(ChaincodeStub stub) {
45+
try {
46+
final List<String> argList = stub.getArgsAsStrings();
47+
final String function = argList.get(0);
48+
final List<String> args = argList.subList(0, argList.size());
49+
50+
switch (function) {
51+
case "init":
52+
case "setMap":
53+
this.mapChaincodeName = args.get(0);
54+
return newSuccessResponse();
55+
case "put":
56+
stub.invokeChaincodeWithStringArgs(this.mapChaincodeName, args);
57+
case "query":
58+
return doQuery(stub, args);
59+
default:
60+
return newBadRequestResponse(format("Unknown function: %s", function));
61+
}
62+
} catch (Throwable e) {
63+
return newInternalServerErrorResponse(e);
64+
}
65+
}
66+
67+
private Response doQuery(ChaincodeStub stub, List<String> args) {
68+
final Response response = stub.invokeChaincodeWithStringArgs(this.mapChaincodeName, args);
69+
if(response.getStatus() == Status.SUCCESS_VALUE) {
70+
return newSuccessResponse(String.format("\"%s\" (queried from %s chaincode)", response.getPayload().toStringUtf8(), this.mapChaincodeName));
71+
} else {
72+
return response;
73+
}
5574
}
5675

5776
public static void main(String[] args) throws Exception {
5877
new LinkExample().start(args);
59-
//new Example().start();
6078
}
6179

6280
@Override
6381
public String getChaincodeID() {
6482
return "link";
6583
}
6684

67-
private List<ByteString> toByteStringList(String[] args) {
68-
LinkedList<ByteString> result = new LinkedList();
69-
for (int i=0; i<args.length; ++i) {
70-
result.add(ByteString.copyFrom(args[i].getBytes(StandardCharsets.UTF_8)));
71-
}
72-
return result;
73-
}
7485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
Copyright DTCC, IBM 2016, 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
18+
buildscript {
19+
repositories {
20+
mavenLocal()
21+
mavenCentral()
22+
jcenter()
23+
}
24+
}
25+
26+
plugins {
27+
id "java"
28+
id "eclipse"
29+
id "application"
30+
}
31+
32+
33+
task printClasspath {
34+
doLast {
35+
configurations.testRuntime.each { println it }
36+
}
37+
}
38+
39+
archivesBaseName = "chaincode"
40+
mainClassName="example.Example04"
41+
42+
run {
43+
if (project.hasProperty("appArgs")) {
44+
args = Eval.me(appArgs)
45+
}
46+
}
47+
48+
sourceSets {
49+
main {
50+
java {
51+
srcDir 'src/main/java'
52+
}
53+
}
54+
}
55+
56+
repositories {
57+
mavenLocal()
58+
mavenCentral()
59+
}
60+
61+
62+
jar.doFirst {
63+
destinationDir=file("${buildDir}")
64+
manifest {
65+
attributes (
66+
'Main-Class': mainClassName,
67+
'Class-Path': configurations.runtime.collect { "libs/"+"$it.name" }.join(' ')
68+
)
69+
}
70+
}
71+
72+
task copyToLib(type: Copy) {
73+
into "$buildDir/libs"
74+
from configurations.runtime
75+
}
76+
build.finalizedBy(copyToLib)
77+
78+
79+
dependencies {
80+
compile 'io.grpc:grpc-all:0.13.2'
81+
compile 'commons-cli:commons-cli:1.3.1'
82+
compile 'org.hyperledger:shim-client:1.0'
83+
compile 'org.glassfish:javax.json:1.1.0-M1'
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright IBM 2017 All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package example;
16+
17+
import static java.lang.String.format;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newBadRequestResponse;
20+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newInternalServerErrorResponse;
21+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newSuccessResponse;
22+
23+
import java.util.Arrays;
24+
import java.util.List;
25+
26+
import javax.json.Json;
27+
28+
import org.hyperledger.fabric.protos.common.Common.Status;
29+
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
30+
import org.hyperledger.fabric.shim.ChaincodeBase;
31+
import org.hyperledger.fabric.shim.ChaincodeStub;
32+
33+
public class Example04 extends ChaincodeBase {
34+
35+
@Override
36+
public Response init(ChaincodeStub stub) {
37+
// expects to be called with: { "init", key, value }
38+
try {
39+
40+
final List<String> args = stub.getArgsAsStrings();
41+
if(args.size() != 3) {
42+
return newBadRequestResponse("Incorrect number of arguments. Expecting \"init\" plus 2 more.");
43+
}
44+
45+
final String key = args.get(1);
46+
final int value = Integer.parseInt(args.get(2));
47+
stub.putState(key, String.valueOf(value));
48+
49+
} catch (NumberFormatException e) {
50+
return newBadRequestResponse("Expecting integer value for sum");
51+
} catch (Throwable t) {
52+
return newInternalServerErrorResponse(t);
53+
}
54+
55+
return newSuccessResponse();
56+
}
57+
58+
@Override
59+
public Response invoke(ChaincodeStub stub) {
60+
// expects to be called with: { "invoke"|"query", chaincodeName, key }
61+
try {
62+
final List<String> argList = stub.getArgsAsStrings();
63+
final String function = argList.get(0);
64+
final String[] args = argList.stream().skip(1).toArray(String[]::new);
65+
66+
switch (function) {
67+
case "invoke":
68+
return doInvoke(stub, args);
69+
case "query":
70+
return doQuery(stub, args);
71+
default:
72+
return newBadRequestResponse(format("Unknown function: %s", function));
73+
}
74+
} catch (NumberFormatException e) {
75+
return newBadRequestResponse(e.toString());
76+
} catch (AssertionError e) {
77+
return newBadRequestResponse(e.getMessage());
78+
} catch (Throwable e) {
79+
return newInternalServerErrorResponse(e);
80+
}
81+
}
82+
83+
84+
private Response doQuery(ChaincodeStub stub, String[] args) {
85+
switch (args.length) {
86+
case 1:
87+
case 2:
88+
case 3: {
89+
final String key = args[0];
90+
final int value = Integer.parseInt(stub.getState(key));
91+
return newSuccessResponse(
92+
Json.createObjectBuilder()
93+
.add("Name", key)
94+
.add("Amount", value)
95+
.build().toString().getBytes(UTF_8)
96+
);
97+
}
98+
case 4: {
99+
final String chaincodeToCall = args[1];
100+
final String key = args[2];
101+
final String channel = args[3];
102+
103+
// invoke other chaincode
104+
final Response response = stub.invokeChaincodeWithStringArgs(chaincodeToCall, Arrays.asList(new String[]{ "query", key}), channel);
105+
106+
// check for error
107+
if(response.getStatus() != Status.SUCCESS_VALUE) {
108+
return response;
109+
}
110+
111+
// return payload
112+
return newSuccessResponse(response.getPayload().toByteArray());
113+
}
114+
default:
115+
throw new AssertionError("Incorrect number of arguments. Expecting 1 or 4.");
116+
}
117+
}
118+
119+
private Response doInvoke(ChaincodeStub stub, String[] args) {
120+
if(args.length != 3 && args.length != 4) throw new AssertionError("Incorrect number of arguments. Expecting 3 or 4");
121+
122+
// the other chaincode's name
123+
final String nameOfChaincodeToCall = args[0];
124+
125+
// other chaincode's channel
126+
final String channel;
127+
if(args.length == 4) {
128+
channel = args[3];
129+
} else {
130+
channel = null;
131+
}
132+
133+
// state key to be updated
134+
final String key = args[1];
135+
136+
// state value to be stored upon success
137+
final int value = Integer.parseInt(args[2]);
138+
139+
// invoke other chaincode
140+
final Response response = stub.invokeChaincodeWithStringArgs(nameOfChaincodeToCall, Arrays.asList("invoke", "a", "b", "10"), channel);
141+
142+
// check for error
143+
if(response.getStatus() != Status.SUCCESS_VALUE) {
144+
return newInternalServerErrorResponse("Failed to query chaincode.", response.getPayload().toByteArray());
145+
}
146+
147+
// update the ledger to indicate a successful invoke
148+
stub.putState(key, String.valueOf(value));
149+
150+
// return the called chaincode's response
151+
return response;
152+
}
153+
154+
@Override
155+
public String getChaincodeID() {
156+
return "Example04";
157+
}
158+
159+
public static void main(String[] args) throws Exception {
160+
new Example04().start(args);
161+
}
162+
163+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
Copyright DTCC, IBM 2016, 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
18+
buildscript {
19+
repositories {
20+
mavenLocal()
21+
mavenCentral()
22+
jcenter()
23+
}
24+
}
25+
26+
plugins {
27+
id "java"
28+
id "eclipse"
29+
id "application"
30+
}
31+
32+
33+
task printClasspath {
34+
doLast {
35+
configurations.testRuntime.each { println it }
36+
}
37+
}
38+
39+
archivesBaseName = "chaincode"
40+
mainClassName="example.Example05"
41+
42+
run {
43+
if (project.hasProperty("appArgs")) {
44+
args = Eval.me(appArgs)
45+
}
46+
}
47+
48+
sourceSets {
49+
main {
50+
java {
51+
srcDir 'src/main/java'
52+
}
53+
}
54+
}
55+
56+
repositories {
57+
mavenLocal()
58+
mavenCentral()
59+
}
60+
61+
62+
jar.doFirst {
63+
destinationDir=file("${buildDir}")
64+
manifest {
65+
attributes (
66+
'Main-Class': mainClassName,
67+
'Class-Path': configurations.runtime.collect { "libs/"+"$it.name" }.join(' ')
68+
)
69+
}
70+
}
71+
72+
task copyToLib(type: Copy) {
73+
into "$buildDir/libs"
74+
from configurations.runtime
75+
}
76+
build.finalizedBy(copyToLib)
77+
78+
79+
dependencies {
80+
compile 'io.grpc:grpc-all:0.13.2'
81+
compile 'commons-cli:commons-cli:1.3.1'
82+
compile 'org.hyperledger:shim-client:1.0'
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright IBM 2017 All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package example;
16+
17+
import static java.lang.String.format;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newBadRequestResponse;
20+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newInternalServerErrorResponse;
21+
import static org.hyperledger.fabric.shim.ChaincodeHelper.newSuccessResponse;
22+
23+
import java.util.Arrays;
24+
import java.util.List;
25+
26+
import org.hyperledger.fabric.protos.common.Common.Status;
27+
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
28+
import org.hyperledger.fabric.shim.ChaincodeBase;
29+
import org.hyperledger.fabric.shim.ChaincodeStub;
30+
31+
public class Example05 extends ChaincodeBase {
32+
33+
@Override
34+
public Response init(ChaincodeStub stub) {
35+
// expects to be called with: { "init", key, value }
36+
try {
37+
38+
final List<String> args = stub.getArgsAsStrings();
39+
if(args.size() != 3) {
40+
return newBadRequestResponse("Incorrect number of arguments. Expecting \"init\" plus 2 more.");
41+
}
42+
43+
final String key = args.get(1);
44+
final int value = Integer.parseInt(args.get(2));
45+
stub.putState(key, String.valueOf(value));
46+
47+
} catch (NumberFormatException e) {
48+
return newBadRequestResponse("Expecting integer value for sum");
49+
} catch (Throwable t) {
50+
return newInternalServerErrorResponse(t);
51+
}
52+
53+
return newSuccessResponse();
54+
}
55+
56+
@Override
57+
public Response invoke(ChaincodeStub stub) {
58+
// expects to be called with: { "invoke"|"query", chaincodeName, key }
59+
try {
60+
final List<String> argList = stub.getArgsAsStrings();
61+
final String function = argList.get(0);
62+
final String[] args = argList.stream().skip(1).toArray(String[]::new);
63+
64+
switch (function) {
65+
case "invoke":
66+
return doInvoke(stub, args);
67+
case "query":
68+
return doQuery(stub, args);
69+
default:
70+
return newBadRequestResponse(format("Unknown function: %s", function));
71+
}
72+
} catch (NumberFormatException e) {
73+
return newBadRequestResponse(e.toString());
74+
} catch (AssertionError e) {
75+
return newBadRequestResponse(e.getMessage());
76+
} catch (Throwable e) {
77+
return newInternalServerErrorResponse(e);
78+
}
79+
}
80+
81+
82+
private Response doQuery(ChaincodeStub stub, String[] args) {
83+
// query is the same as invoke, but with response payload wrapped in json
84+
final Response result = doInvoke(stub, args);
85+
if(result.getStatus() == Status.SUCCESS_VALUE) {
86+
return newSuccessResponse(format("{\"Name\":\"%s\",\"Value\":%s}", args[0],result.getPayload().toStringUtf8()));
87+
} else {
88+
return result;
89+
}
90+
}
91+
92+
private Response doInvoke(ChaincodeStub stub, String[] args) {
93+
if(args.length !=2) throw new AssertionError("Incorrect number of arguments. Expecting 2");
94+
95+
// the other chaincode's id
96+
final String chaincodeName = args[0];
97+
98+
// key containing the sum
99+
final String key = args[1];
100+
101+
// query other chaincode for value of key "a"
102+
final Response queryResponseA = stub.invokeChaincodeWithStringArgs(chaincodeName, Arrays.asList(new String[] {"query", "a"}));
103+
104+
// check for error
105+
if(queryResponseA.getStatus() != Status.SUCCESS_VALUE) {
106+
return newInternalServerErrorResponse("Failed to query chaincode.", queryResponseA.getPayload().toByteArray());
107+
}
108+
109+
// parse response
110+
final int a = Integer.parseInt(queryResponseA.getPayload().toStringUtf8());
111+
112+
// query other chaincode for value of key "b"
113+
final Response queryResponseB = stub.invokeChaincodeWithStringArgs(chaincodeName, "query", "b");
114+
115+
// check for error
116+
if(queryResponseB.getStatus() != Status.SUCCESS_VALUE) {
117+
return newInternalServerErrorResponse("Failed to query chaincode.", queryResponseB.getPayload().toByteArray());
118+
}
119+
120+
// parse response
121+
final int b = Integer.parseInt(queryResponseB.getPayload().toStringUtf8());
122+
123+
// calculate sum
124+
final int sum = a + b;
125+
126+
// write new sum to the ledger
127+
stub.putState(key, String.valueOf(sum));
128+
129+
// return sum as string in payload
130+
return newSuccessResponse(String.valueOf(sum).getBytes(UTF_8));
131+
}
132+
133+
@Override
134+
public String getChaincodeID() {
135+
return "Example05";
136+
}
137+
138+
public static void main(String[] args) throws Exception {
139+
new Example05().start(args);
140+
}
141+
142+
}

0 commit comments

Comments
 (0)
Please sign in to comment.