Skip to Content

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 S/4HANA Cloud SDK.

Be aware: At the time of writing the SAP Cloud Platform Blockchain Hyperledger Fabric service is available to customers, but not to trial account users (2018-06-22).

Note: This post is part of a series. For a complete overview visit the SAP S/4HANA 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:

  1. Introduce the concept of SAP Cloud Platform Blockchain and its technology variant Hyperledger Fabric and point out their significance.
  2. 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 with 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:

  1. First create a node service
  2. Create the channel in the node dashboard
  3. Create a service for the channel
  4. 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 go to ‘Service Bindings’ and choose ‘Bind Service’. Select ‘Service from the catalog’ and search for ‘fabric’ in the following screen:

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’ or ‘dev’ plan, and continue with ‘Next’. 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’ to 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 bindings screen, or via the service instances screen 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=l1AtLe )

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.

Now your SAP Cloud Platform Hyperledger Fabric setup is ready for integration with your SAP S/4HANA side-by-side extension using SAP S/4HANA Cloud SDK!

Access the chaincode from the SAP S/4HANA 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 # required to make eclipse:eclipse or idea:idea work

mvn eclipse:eclipse

mvn idea:idea

Then, for simplicity’s sake, create these two classes in the same folder: BlockchainInvocationType and FabricService . They facilitate access from Java:

public enum BlockchainInvocationType
{
   /**
    * Invoke ensures that a Fabric transaction is set up around a call, permitting modifications during the execution.
    */
   INVOKE("invoke"),
   
   /**
    * Query doesn't permit modifications but is very lightweight, executing much faster and without involving other 
    * nodes.
    */
   QUERY("query");

   private final String pathParamValue;

   BlockchainInvocationType( String pathParamValue ) {
      this.pathParamValue = pathParamValue;
   }

   public String getPathParamValue() {
      return pathParamValue;
   }
}

The FabricService class:

public class FabricService
{

    private static final Logger logger = CloudLoggerFactory.getLogger(FabricService.class);

    private ScpCfService cfService;

    public FabricService( ScpCfService cfService ) {
        this.cfService = cfService;
    }

    public static FabricService createByGettingTokenViaCfServicesConfig() throws ShouldNotHappenException 
    {
        return getFabricService("hyperledger-fabric");
    }

    /**
     * Run a blockchain chaincode...
     */
    public String invokeOrQuery(BlockchainInvocationType invocationType, String chaincodeId,
                                String function, String... args) throws Exception 
    {
        logger.trace("{} {}:{}({})", invocationType, chaincodeId, function, Arrays.asList(args));

        final HttpPost chaincodeEndpointRequest = new HttpPost(
                new URI(getServiceUrl() + "/chaincodes/" + chaincodeId
                        + "/latest/" + invocationType.getPathParamValue()).normalize());

        cfService.addBearerTokenHeader(chaincodeEndpointRequest);
        chaincodeEndpointRequest.setHeader("Accept", "application/json;charset=UTF-8");

        final JsonObject requestBody = new JsonObject();
        requestBody.addProperty("function", function);
        requestBody.add("arguments", new Gson().toJsonTree(args));
        requestBody.addProperty("async", false);

        final String json = new Gson().toJson(requestBody);
        logger.trace("Request: {}", json);
        chaincodeEndpointRequest.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
        chaincodeEndpointRequest.setHeader("Content-Type", "application/json");

        try {
            final HttpResponse response = HttpClientAccessor.getHttpClient().execute(chaincodeEndpointRequest);
            final StatusLine statusLine = response.getStatusLine();

            final String responsePayload = HttpEntityUtil.getResponseBody(response);
            logger.debug("Response status: {}", statusLine);
            logger.trace("Response status: {}, content: {}", statusLine, responsePayload);

            switch (statusLine.getStatusCode()) {
                case HttpStatus.SC_OK:
                    return responsePayload;

                case HttpStatus.SC_UNAUTHORIZED:
                case HttpStatus.SC_FORBIDDEN:
                    throw new AuthenticationException("Access token already expired or simply wrong: "
                            + "Service request denied with status: " + statusLine);
                default:
                    throw new Exception("Failed to execute request to function " + function + ": "
                            + "Service request failed with status: " + statusLine);
            }
        } finally {
            chaincodeEndpointRequest.releaseConnection();
        }
    }

    static FabricService getFabricService( final String serviceType )
            throws ShouldNotHappenException
    {
        try {
            final ScpCfService cfService = ScpCfService.of(serviceType, null, "credentials/oAuth/url",
                    "credentials/oAuth/clientId", "credentials/oAuth/clientSecret", "credentials/serviceUrl");

            return new FabricService(cfService);

        } catch (ShouldNotHappenException e) {
            throw e;
        } catch (final Exception e) {
            throw new ShouldNotHappenException("Failed to setup Blockchain service: " + e.getMessage(), e);
        }
    }

    public String getServiceUrl() {
        return cfService.getServiceLocationInfo();
    }

    @Override
    public String toString()
    {
        return "FabricService{" +
                "cfService=" + cfService +
                '}';
    }
}

FabricService makes use of SAP S/4HANA Cloud SDK features such as:

  • ScpCfService class, providing access to SAP Cloud Platform Cloud Foundry services bound to an application
  • HTTP client management

With these two helpers creating the servlet becomes straightforward:

@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.createByGettingTokenViaCfServicesConfig();

            if (write) {
                // throws on error
                fabricService.invokeOrQuery(
                        BlockchainInvocationType.INVOKE, // invoke ensures that a Fabric transaction is set up
                        getChaincodeId(), "store", value);

            } else {
                final String result = fabricService.invokeOrQuery(
                        BlockchainInvocationType.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()
    {
        try {
            final Optional<String> simple_chaincode_id = ((ScpCfCloudPlatform) CloudPlatformAccessor.getCloudPlatform())
                    .getEnvironmentVariable(SIMPLE_CHAINCODE_ID);
            if (simple_chaincode_id.isPresent()) {
                return simple_chaincode_id.get();
            }
            throw new ShouldNotHappenException(SIMPLE_CHAINCODE_ID + " environment variable not set.");

        } catch (ClassCastException cce) {
            throw new ShouldNotHappenException("Not part of a SAP Cloud Platform Cloud Foundry application, " +
                    "please only use in that environment.");
        }
    }
}

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>

# VCAP_SERVICES needs mocking too if you test locally.
# Get the values 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": "<YOUR CHANNEL ID", "serviceUrl": "https://hyperledger-fabric.cfapps.sap.hana.ondemand.com/api/v1", "documentationUrl": "https://hyperledger-fabric.cfapps.sap.hana.ondemand.com/api/v1/docs", "oAuth": { "clientId": "<YOUR CLIENT ID>", "clientSecret": "<YOUR CLIENT SECRET>", "url": "<YOUR OAUTH URL>" } }, "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/hello . 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

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/hello ).

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!

 

 

 

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Hakan Kocatepe

    Hi,

    Thank you for your posting.It is very useful for SCP on BaaS. Is it possible that trial account can access Hyperledger Fabric by requesting SAP Support ?
    How can I contact to SAP Support If I have trial account ?

    Best Regards,
    Hakan

    (1) 
    1. Gregory Misiorek

      Hi Hakan,

      i believe the ‘key’ is to follow this instruction: “increase the quota to your global account and setup the required entitlements for the SAP Cloud Platform sub account you are using.”

      btw, i saw Georg presenting about blockchain at TechEd last year and he was very enthusiastic about the topic, but some decisions to open it up to the wide ecosystem may not be his.

      happy blockchaining, greg

       

      (0) 
    2. Georg Koester Post author

      Hi Hakan,

      I have to say that the SAP Cloud Platform Blockchain Hyperledger Fabric service(s) are not available to trial account users. Customers have access to these services. I included a note regarding this in the text.

      Sorry to not have better news,
      Georg

      (1) 

Leave a Reply