Update November 2020: The Hyperledger Fabric service is being retired from the SAP Cloud Platform and no longer available for trial, see here:
Service catalog then click 'Service Plan'. SAP is moving to provide higher level services. If you need this low-level functionality please review the offers of Hyperledger Fabric services of our multicloud partners.
OUTDATED:
The following steps will explain how to integrate SAP Leonardo Blockchain using Hyperledger Fabric with an SAP Cloud Platform side-by-side extension using the SAP Cloud SDK.
SAP Cloud Platform Blockchain Hyperledger Fabric service is available to customers and on the SAP Cloud Platform trial account.
Disclaimer: This blog post is only applicable for the SAP Cloud SDK version of at most 2.19.2. We plan to continuously migrate these blog posts into our List of Tutorials. Feel free to check out our updated Tutorials on the SAP Cloud SDK.
Updated May 2019: SAP S/4HANA Cloud SDK transformed to become the SAP Cloud SDK: All references have been updated.
Updated Oct 2018: Using Hyperledger Fabric became even more simple! Newly added FabricService class is the key. See below!
Note: This post is part of a series. For a complete overview visit the
SAP Cloud SDK Overview. Furthermore, an extended version of this blogpost is part of the book
Extending SAP S/4HANA.
Goal of this Blog Post
This blog post covers the following steps :
- Introduce the concept of SAP Cloud Platform Blockchain and its technology variant Hyperledger Fabric and point out their significance.
- Build a microservice reading from a Hyperledger Fabric chaincode on SAP Cloud Platform Blockchain.
SAP Cloud Platform Blockchain using Hyperledger Fabric
Blockchain is a concept and an architecture based on distributed ledger technology to record and share data across a multi-party network. It integrates data from multiple sources and creates a single, trustful truth. In doing so, it promises to provide powerful advantages for businesses, leading to increased efficiency and simplified processes.
Cases such as Blockchain-based
pallet management and
Farm to Consumer traceability implemented by SAP showcase the potential. Blockchain will change the way we do business - from point to point interaction to a true network of business objects and entities.
SAP drives the value-adding Blockchain with products such as
SAP Cloud Platform Blockchain, which provides maximum flexibility with the support of multiple advanced blockchain technologies like Hyperledger Fabric and MultiChain. In this article you will learn how to integrate one of those technologies with your S/4HANA side-by-side extension: Hyperledger Fabric. The availability of multiple technologies provides you options to follow and be on top of the continuing development of blockchain technology.
Outline of this post
The post will help you to set up the technical integration between a S/4HANA side-by-side extension on the SAP Cloud Platform and the SAP Cloud Platform Hyperledger Fabric Blockchain service. The first step will be creating a skeleton side-by-side extension project and deploying it on the SAP Cloud Platform to test the viability of your development environment and create the application's environment on the SAP Cloud Platform to which blockchain services can be bound. Then required SAP Cloud Platform Blockchain services will be created and bound to the application. A chaincode will then be deployed to a channel service, which is similar to creating an API to a part of the Hyperledger Fabric blockchain. Then finally the access to the chaincode will be added to the Java code of the side-by-side extension.
Creating the side-by-side extension
The starting point is the generation of the hello-world application template with maven (see Step 3
https://blogs.sap.com/2017/05/19/step-3-with-sap-s4hana-cloud-sdk-helloworld-on-scp-cloudfoundry/ for a detailed introduction):
mvn archetype:generate -DarchetypeGroupId=com.sap.cloud.s4hana.archetypes \
-DarchetypeArtifactId=scp-cf-tomee -DarchetypeVersion=LATEST
The project is directly runnable after its creation: A hello world servlet is provided for quick and easy testing. This is what we will leverage now to quickly continue setting up and testing our environment. Compile and run the code for a quick initial test (in application subdirectory):
mvn package
mvn tomee:run
# or: mvn tomee:debug
The service should be accessible at
http://localhost:8080/hello . Continue to deploy it to SAP Cloud Platform (in main directory, where manifest.yml is located):
cf push
The application host name will be provided by the output on the command line (mine was named: blockchain-fabric-blog-tomee-aidless-solver.cfapps.sap.hana.ondemand.com, making it accessible at
http://blockchain-fabric-blog-tomee-aidless-solver.cfapps.sap.hana.ondemand.com/hello ).
Create the required blockchain services
On SAP Cloud Platform you have a wide variety of services you can choose to use in your applications. A couple of them belong to SAP Cloud Platform Blockchain:
- Hyperledger Fabric Node
- Hyperledger Fabric Channel
- MultiChain ...
For this blog post we'll use the Hyperledger Fabric Node and Channel services of SAP Cloud Platform Blockchain. A node represents a single machine connected to a blockchain network, and a channel is one communications channel on such a network. So a node can host multiple channels. And that's why we start with the creation of the node, and then setup a channel on the node. Any chaincode we create will be deployed to a channel on SAP Cloud Platform Blockchain.
So here we will follow these steps to setup the required services:
- First create a node service
- Create the channel in the node dashboard
- Create a service for the channel
- Bind the channel service to the application
Afterwards we are going to deploy a simple chaincode example on the channel.
Go to the SAP Cloud Platform Cockpit and find your newly deployed application. Then navigate to the screen for the space your application was created in. Then go to 'Service Marketplace' and search for 'fabric', compare the screenshot below.
You'll reach the overview screen for the Hyperledger Fabric service. Then go to the 'Instances' screen via the menu on the left, and choose 'New Instance'.
If your organization or your trial account don't have quota to use the Hyperledger Fabric service, please contact SAP support or your internal SAP Cloud Platform account responsible to increase the quota to your global account and setup the required entitlements for the SAP Cloud Platform sub account you are using. Be also aware that you can reuse one node for different channels - so you don't have to setup new node services for every trial you are running.
Select the Hyperledger Fabric service and continue with 'Next'. Then on the next screen select the 'node', 'dev', or 'dev-beta' plan, and continue with 'Next'. For a test 'dev' and 'dev-beta' should prove the correct choice as they provide cheap, or even free-of-charge, development environments. Now you might run into trouble of quota and/or entitlements are not sufficient. Please contact SAP support or your internal SAP Cloud Platform account responsible.
For this trial you don't need any parameters, so you can jump the next screen with 'Next' and not bind this service to any application. The channel service we'll create afterwards gets bound to an application - this service actually cannot be bound to an application. The you end up on the summary page. Choose a service name and hit 'Finish'.
Then you will be able to access the node dashboard via a button on the service instances screens of the Hyperledger Fabric services, or that of the Cloud Foundry space that your application was deployed in:
In the node dashboard please open the 'Channels' screen. Here please hit the 'Create Channel' button to create a new channel for our chaincode deployment:
In the dialog coming up please choose a name and feel free to add a description for your channel:
The new channel will be added to the list of channels. Now what is still missing is a way to make this channel available to applications. This is accomplished with the creation of a service representing the channel in the SAP Cloud Platform Cockpit - a Cloud Foundry service which in turn can be bound to an application. To create the service please hit the button indicated below:
After the channel service creation the button will morph to a button providing access to the channel dashboard. You can see testchannel4 in the above screenshot sporting that dashboard button already. You might have to reload the channels listing to make the button appear.
Now you are ready to deploy chaincode to the channel. Let's have a look at a chaincode example now.
Deploy an example chaincode
This is a fast-paced overview that gets you running in no time - but it is not a complete introduction into the topic of chaincode development. Please see here for the
documentation on chaincode development on SAP Cloud Platform Blockchain.
The chaincode sample is a very straightforward proof-of-existence scenario. The scenario is even too straightforward in that it doesn't report the first time a value was written to the chain - fixing that is left as an exercise to the reader.
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
var logger = shim.NewLogger("simpleChaincode")
type SimpleChaincode struct {
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting simple chaincode: %s", err)
}
}
// Init is being executed during initial setup and also for an upgrade transaction
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
_, args := stub.GetFunctionAndParameters()
logger.Debugf("Simple Init(%s) called", args)
return shim.Success(nil)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
method, args := stub.GetFunctionAndParameters()
logger.Debug("Invoke execution for method %s", method)
switch method {
case "store":
return t.store(stub, args)
case "readSingle":
return t.readSingle(stub, args)
default:
return shim.Error("Unknown method")
}
}
func (t *SimpleChaincode) store(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Needs 1 arg: value")
}
value := args[0]
singleOneByte := []byte {byte(1)}
putErr := stub.PutState(value, singleOneByte)
if putErr != nil {
logger.Errorf("PutState failed in store: %s", putErr)
return shim.Error("Could not add entry: Failure to write to chain")
}
return shim.Success(nil)
}
func (t *SimpleChaincode) readSingle(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Needs 1 arg: value")
}
value := args[0]
chainValue, err := stub.GetState(value)
if err != nil {
logger.Errorf("Checking status for '%s' failed: %s", value, err)
return shim.Error("Querying failed")
}
if (chainValue != nil) {
return shim.Success([]byte("true"))
} else {
return shim.Success([]byte("false"))
}
}
The two methods
store
and
readSingle
provide the possibility to add string information to the chain and to verify that a specific string has been written to the chain. SAP Cloud Platform Blockchain facilitates deployment of chaincode to a Hyperledger Fabric node considerably. The platform handles compilation, version upgrade, and provides a testing frontend. For this to work the chaincode is packaged in a zip, complete with a bit of meta-information. The package and content with our sample chaincode are made available for your convenience.
Download the
simple chaincode example zip here ( https://sap-my.sharepoint.com/:f:/p/georg_koester/EuwR5AyRaatAjHeWj4XxxJQBF61N35GldeC1BAjInvzN4w?e=3... ) .
Now open the channel dashboard via the node dashboard.
In the channel dashboard, open the 'Chaincode' screen and hit the 'Deploy Chaincode' button:
In the displayed dialog you can then provide the zip file and a description. I would recommend to insert the version in the description field to make management in the UI easier:
The sample chaincode does not require init arguments, so just hit 'Deploy' afterwards. You should be presented with a screen similar to this:
The testing UI available via the button is explained the
SAP Cloud Platform Hyperledger Fabric documentation: Testing the Chaincode. It is dependent on an OpenAPI spec file (there is an example in the subdirectory of the provided ZIP).
Please remember the displayed Chaincode ID - you will need to make it available to the application in its environment later.
Bind the channel service to the application
Finally, go to the application screen in SAP Cloud Platform Cockpit, then the 'Service Bindings' screen, select 'New Binding' and add the existing Hyperledger Fabric channel service to your application. The application should exist if you followed this deep dive. If you don't have an existing application go back to step 'Creating the side-by-side extension'.
Now your SAP Cloud Platform Hyperledger Fabric setup is ready for integration with your SAP S/4HANA side-by-side extension using SAP Cloud SDK!
Access the chaincode from the SAP Cloud SDK side-by-side extension
Open the project skeleton generated before with your favorite IDE, and browse to the
HelloWorldServlet
. You can generate project files with maven, too:
# in main directory:
mvn install
# install is required to make eclipse:eclipse or idea:idea work
mvn eclipse:eclipse
mvn idea:idea
The
FabricService
class of the SAP Cloud SDK facilitates access to the SAP Cloud Platform Blockchain Hyperledger Fabric service.
FabricService
makes use of SAP Cloud SDK features such as:
ScpCfService
class, providing access to SAP Cloud Platform Cloud Foundry services bound to an application
- HTTP client management
It is available in an additional dependency that you have to add to the application/pom.xml :
...
<dependency>
<groupId>com.sap.cloud.s4hana</groupId>
<artifactId>s4hana-all</artifactId>
</dependency>
<!-- Dependency added for SAP Cloud Platform Blockchain service access facilitation -->
<dependency>
<groupId>com.sap.cloud.s4hana.services</groupId>
<artifactId>scp-blockchain</artifactId>
</dependency>
...
With this helper creating the servlet becomes straightforward:
import com.google.common.base.Strings;
import com.sap.cloud.sdk.cloudplatform.CloudPlatformAccessor;
import com.sap.cloud.sdk.cloudplatform.ScpCfCloudPlatform;
import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import org.slf4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/fabric_simple")
public class FabricSimpleServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private static final Logger logger = CloudLoggerFactory.getLogger(FabricDemoServlet.class);
public static final String SIMPLE_CHAINCODE_ID = "SIMPLE_CHAINCODE_ID";
@Override
protected void doGet( final HttpServletRequest request, final HttpServletResponse response )
throws ServletException, IOException
{
try {
String value = request.getParameter("value");
if (Strings.isNullOrEmpty(value)) {
throw new IllegalArgumentException("Missing value get parameters.");
}
boolean write = Boolean.parseBoolean(request.getParameter("write"));
final FabricService fabricService = FabricService.create();
if (write) {
// throws on error
fabricService.invokeOrQuery(
FabricInvocationType.INVOKE, // invoke ensures that a Fabric transaction is set up
getChaincodeId(), "store", value);
} else {
final String result = fabricService.invokeOrQuery(
FabricInvocationType.QUERY, // query doesn't permit modifications but is very lightweight
getChaincodeId(), "readSingle", value);
logger.debug("Response: {}", result);
response.getWriter().write(result);
}
} catch (Exception e) {
logger.error("Failure: " + e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().println("Error: " + e.getMessage());
}
}
public String getChaincodeId()
{
final Optional<String> simple_chaincode_id = ScpCfCloudPlatform.getInstanceOrThrow()
.getEnvironmentVariable(SIMPLE_CHAINCODE_ID);
if (simple_chaincode_id.isPresent()) {
return simple_chaincode_id.get();
}
throw new ShouldNotHappenException(SIMPLE_CHAINCODE_ID + " environment variable not set.");
}
}
Now you can compile and run the code for a quick initial test (in application subdirectory):
mvn package
export ALLOW_MOCKED_AUTH_HEADER=true
# or windows: set ALLOW_MOCKED_AUTH_HEADER=true
export SIMPLE_CHAINCODE_ID=<chaincode id available in channel dashboard or chaincode deployment result as shown above>
# or windows: set SIMPLE_CHAINCODE_ID=<chaincode id available in channel dashboard or chaincode deployment result as shown above>
# VCAP_SERVICES needs mocking too if you test locally.
# Get the values values for the locations marked <EDIT> from the SAP Cloud Platform Cockpit in the application screen under environment variables:
export VCAP_SERVICES='{ "hyperledger-fabric": [{ "name": "test-fabric.testchannel", "instance_name": "test-fabric.testchannel", "binding_name": null, "credentials": { "type": "hyperledger-fabric", "channelId": "<EDIT>", "serviceUrl": "<EDIT>", "documentationUrl": "https://help.sap.com/viewer/p/HYPERLEDGER_FABRIC", "oAuth": { "clientId": "<EDIT>", "clientSecret": "<EDIT>", "url": "<EDIT>" } }, "syslog_drain_url": null, "volume_mounts": [], "label": "hyperledger-fabric", "provider": null, "plan": "channel", "tags": [ "channel" ] }] }'
# on windows: set VCAP_SERVICES={ "hyperledger-fabric": [{ "name": "test-fabric.testchannel", "instance_name": "test-fabric.testchannel", "binding_name": null, "credentials": { "type": "hyperledger-fabric", "channelId": "<EDIT>", "serviceUrl": "<EDIT>", "documentationUrl": "https://hyperledger-fabric.cfapps.sap.hana.ondemand.com/api/v1/docs", "oAuth": { "clientId": "<EDIT>", "clientSecret": "<EDIT>", "url": "<EDIT>" } }, "syslog_drain_url": null, "volume_mounts": [], "label": "hyperledger-fabric", "provider": null, "plan": "channel", "tags": [ "channel" ] }] }
mvn tomee:run
# or: mvn tomee:debug
The service should be accessible at
http://localhost:8080/fabric_simple?value=testvalue . You can even deploy it to SAP Cloud Platform (in main directory, where manifest.yml is located):
# show your apps in current space - this assumes you deployed the skeleton as recommended above
cf apps
# set the environment variable indicating which chaincode is to be accessed:
cf set-env <your app name> SIMPLE_CHAINCODE_ID "<chaincode id from chaincode deployment above>"
cf push
Be aware that also here, if you don't set up the security environment required for production that provides tenant information (see
Secure your application on SAP Cloud Platform Cloud Foundry ) you will need to mock auth headers:
cf set-env <enter your app name> ALLOW_MOCKED_AUTH_HEADER true
# after the change of the env variable you need to restart the application:
cf restart <enter your app name>
The application host name will be provided by the output on the command line (mine was named:blockchain-fabric-blog-tomee-aidless-solver.cfapps.sap.hana.ondemand.com, making it accessible at
http://blockchain-fabric-prototyping-blog-tomee-aidless-solver.cfapps.sap.hana.ondemand.com/fabric_s... - SAP-internal URL, not accessible outside ).
Write a value with ?value=somevalue&write=true .
The resulting microservice is most simple but hopefully very transparent and instructive. Here is a test from a browser:
This blog post has given you an introduction to starting your own blockchain integration project with SAP Cloud Platform, and the environment facilitates connecting with SAP S/4HANA easily. I am very keen to see what you will be doing with these powerful tools!