Tutorial Invoking Chaincode from Hyperledger Fabric Java SDK

Lukas Kolisko
3 min readFeb 26, 2018

--

In the previous post we learned how to do some basic interactions we fabric-ca and fabric using the fabric-sdk-java. In this post we will make one step further and actually invoke a chaincode.

The prerequisite is fabcar network up and running. If you do not have it, then please follow this tutorial http://hyperledger-fabric.readthedocs.io/en/release/write_first_app.html .

The example code is available at https://github.com/lkolisko/scratch.git .

Development Environment

  1. git clone https://github.com/lkolisko/scratch.git demo2
  2. cd demo2/hyperledger/fabric-sdk-java-scratch
  3. … and open in your favorite IDE

The tutorial is reusing some code explained in the previous post . If you did not read that tutorial I highly suggest taking that exercise first.

Open lkolisko.hyperledger.example.HFJavaSDKChainCodeInvocationExample and let’s follow the code step by step.

Initialization

The initialization creates fabric-ca client and enrolls admin. This step is not necessary if you already have key-pair and certificate signed by the fabric-ca. Then we set admin user into the context and initialize channel object.

HFCAClient caClient =
getHfCaClient("http://localhost:7054", null);
AppUser user = getAdmin(caClient);
HFClient client = getHfClient();
client.setUserContext(user);
Channel channel = getChannel(client);

An important step that must not be forgotten is registering the event hub.

// initialize channel
// peer name and endpoint in fabcar network
Peer peer = client.newPeer("peer0.org1.example.com", "grpc://localhost:7051");
// eventhub name and endpoint in fabcar network
EventHub eventHub = client.newEventHub("eventhub01", "grpc://localhost:7053");
// orderer name and endpoint in fabcar network
Orderer orderer = client.newOrderer("orderer.example.com", "grpc://localhost:7050");
// channel name in fabcar network
Channel channel = client.newChannel("mychannel");
channel.addPeer(peer);
channel.addEventHub(eventHub);
channel.addOrderer(orderer);
channel.initialize();
return channel;

This is required to handle transaction events coming back from fabric. If you forget to register event hub, then the code will wait until timeout on the event from the orderer.

DEBUG Channel — Channel mychannel successful sent to Orderer transaction id: 9570f… 

Invoking query for all cars

The next step is invoking query for all cars. This is exactly the same as in the previous post, therefore I am not going to explain it here. The only difference is that we parse the JSON response and built map of CarRecord objects. We are using standard Json support of the JDK.

// get channel instance from client
Channel channel = client.getChannel("mychannel");
// create chaincode request
QueryByChaincodeRequest qpr = client.newQueryProposalRequest();
// build cc id providing the chaincode name. Version is omitted here.
ChaincodeID fabcarCCId = ChaincodeID.newBuilder().setName("fabcar").build();
qpr.setChaincodeID(fabcarCCId);
// CC function to be called
qpr.setFcn("queryAllCars");
Collection<ProposalResponse> responses = channel.queryByChaincode(qpr);
for (ProposalResponse response : responses) {
if (response.isVerified() && response.getStatus() == ChaincodeResponse.Status.SUCCESS) {
ByteString payload = response.getProposalResponse().getResponse().getPayload();
try (JsonReader jsonReader = Json.createReader(new ByteArrayInputStream(payload.toByteArray()))) {
// parse response
JsonArray arr = jsonReader.readArray();
Map<String, CarRecord> cars = new HashMap<>();
for (int i = 0; i < arr.size(); i++) {
JsonObject rec = arr.getJsonObject(i);
CarRecord carRecord = getCar(rec);
cars.put(carRecord.getKey(), carRecord);
}
return cars;
}
} else {
log.error("response failed. status: " + response.getStatus().getStatus());
}
}
return Collections.emptyMap();

Invoking chaincode

Now it finally gets interesting. We are going to add invoke the chaincode and add a new car record to the blockchain. The core is implemented in sendTransaction method.
We create TransactionProposalRequest that carries chaincode identifier, the function we would like to invoke and arguments for the function. We send the transaction proposal to the channel via sendTransactionProposal.

TransactionProposalRequest tpr = client.newTransactionProposalRequest();
ChaincodeID cid = ChaincodeID.newBuilder().setName("fabcar").build();
tpr.setChaincodeID(cid);
tpr.setFcn("createCar");
tpr.setArgs(new String[]{"CAR11", "Skoda", "MB1000", "Yellow", "Lukas"});
Collection<ProposalResponse> responses = channel.sendTransactionProposal(tpr);
List<ProposalResponse> invalid = responses.stream().filter(r -> r.isInvalid()).collect(Collectors.toList());
if (!invalid.isEmpty()) {
invalid.forEach(response -> {
log.error(response.getMessage());
});
throw new RuntimeException("invalid response(s) found");
}
return channel.sendTransaction(responses);

If all responses are fine, then we can proceed with sending the transaction to an orderer. This returns aCompletableFuture<BlockEvent.TransactionEvent> we can handle in our code.

For simplicity, we just request result with a timeout and verify that for validity. We managed to invoke our first chaincode !

BlockEvent.TransactionEvent event = sendTransaction(client, channel).get(60, TimeUnit.SECONDS);
if (event.isValid()) {
log.info("Transacion tx: " + event.getTransactionID() + " is completed.");
} else {
log.error("Transaction tx: " + event.getTransactionID() + " is invalid.");
}

To verify the car is really store we call queryCar with key we passed in there.

Channel channel = client.getChannel("mychannel");
QueryByChaincodeRequest qpr = client.newQueryProposalRequest();
ChaincodeID chainCodeId = ChaincodeID.newBuilder().setName("fabcar").build();
qpr.setChaincodeID(chainCodeId);
qpr.setFcn("queryCar");
qpr.setArgs(new String[]{key});
Collection<ProposalResponse> queryProposals = channel.queryByChaincode(qpr);
...

There is really nothing new compared to queryAllCars to be explained.
Congratulations for having your first fabric chaincode invoked from fabric-sdk-java.

Note: In production code definitely deserves a better error handling and logging that has been omitted here for simplicity. So please do not try this at work!

Happy Chaining Blocks Together!
— Lukas

--

--

Lukas Kolisko

Passionate about science, tech and photography. Interested in machine learning, distributed ledger technology and Java platform.