X

The Supply Chain Management Blog covers the latest
in SCM strategy, technology, and innovation.

Embedded Asset and Derived Key Implementation with Blockchain App Builder for Oracle Blockchain Platform

Gourav Sarkar
Senior Manager, Oracle Blockchain Platform Product Management

We have recently released Blockchain App Builder version 1.4.0 - a low-code development environment for Oracle Blockchain Platform. The App Builder brings speed and business agility to chaincode development, and enables innovation by making blockchain and smart contract accessible to broader development community without requiring specialised skill sets. It also ensures that chaincodes follow best practices by auto generating the core Create, Read (Query), Update and Delete (so called CRUD methods) with consistent methodology that anyone can deploy and use directly through the OBP REST APIs or to invoke from custom functions while building more complex chaincode. With every release of the App Builder, numerous features are added to continue enhancing the Blockchain App Builder as an essential developer tool for developing, deploying and testing of chaincodes. In this release, we have added the following four features:

1.  Embedded asset support in the specification file to enable modular, nested asset structures

2.  Derived key support in the specification file to handle multi-attribute key structures

3.  Enhanced user experience when opening the UI in VS Code extension

4.  User controlled chaincode versioning

The first two features pertaining to embedded assets and use of derived key enable the developers to use the App Builder to develop more complex chaincodes efficiently. The latter two features provide more effective user experience in UI interaction and chaincode versioning.

In this post, I’ll elaborate how embedded assets and derived key can be implemented with Blockchain App Builder - Visual Studio Code extension.

A.  Embedded Asset:

While defining assets in chaincode, an asset can be embedded within another asset to create a more modularised structure. The modular structure can help make code more readable and maintainable. For example, a manufacturer creates a shipment that includes different products. The primary asset will contain the shipment details and it will embed another asset representing the product details. Since one shipment can contain multiple different products, the product details will be declared in the primary asset as an array of embedded assets. 

The implementation of embedded asset can be done in 3 easy steps –

  • Asset declaration in the specification file using embedded assets
  • Creation of custom method logic for transferring primary asset having embedded asset
  • Testing of chaincode with embedded assets

1.  Specification File with an Embedded Asset

The first step is to create a specification file to define the structure of the assets. The "ShipmentDetails" asset is the primary asset and the "ProductDetails" asset is the embedded asset. The type defined in the specification file for ProductDetails attribute (within ShipmentDetails asset) is same as the attribute name (i.e., ProductDetails). It signifies that ProductDetails attribute isn’t just a simple attribute. The type defined for ProductDetails asset has been declared as “embedded” to signify that ProductDetails asset is an embedded asset. Based on the functions listed in the “methods” section under the primary asset ShipmentDetails, App Builder will automatically generate the specified chaincode methods for the primary asset. In this case, createShipmentDetails, getShipmentDetailsById, updateShipmentDetails, and getShipmentDetailsHistoryById will be automatically generated. For embedded assets, the App Builder doesn't create any CRUD methods.

A custom method TransferShipment() has also been defined in the specification file to transfer ownership of shipments across different stakeholders (e.g., from manufacturer to distributors).

assets:

    - name: ShipmentDetails

       properties:

          - name: ShipmentID

             type: string

             mandatory: true

             id: true

          - name: ProductDetails

             type: ProductDetails[]

          - name: Owner

             type: string

            mandatory: true 

      methods:

          crud: [create, getById, update]

          others: [getHistoryById]

    - name: ProductDetails

       type: embedded

       properties:

          - name: ProductID

             type: string

          - name: ProductName

             type: string

          - name: Batch

             type: string[]

          - name: ProductQuantity

             type: number  

customMethods:

    - executeQuery

    - "TransferShipment(ShipmentID string, Owner string)"

2.  Custom Method Logic Creation for Transferring Primary Asset with Embedded Asset

After scaffolding the project in Golang, the custom method (i.e., TransferShipment) logic needs to be updated in controller.go file. The custom method will transfer the ownership of a shipment to different stakeholders (e.g., distributors). The custom method takes 2 inputs – ShipmentID and Owner details. A sample custom code to transfer the shipment ownership has been depicted below.

func (t *Controller) TransferShipment(ShipmentID string, Owner string) (interface{}, error) {

    shipment,err :=t.GetShipmentDetailsById(ShipmentID)

    if err != nil {

        return nil, err

    }

    if shipment.Owner == Owner {

        return `Shipment ID already exists with the Owner ID to be transferred to`,nil

    } else {

        shipment.Owner =  Owner

        t.UpdateShipmentDetails(shipment)

        return `Shipment ID has been transferred to the new Owner`,nil

    }

}

3.  Testing the Chaincode with Embedded Asset

The chaincode can be tested once it’s installed and instantiated. The chaincode testing can be done in 3 easy steps –

i.  Create a shipment with multiple products

ii. Transfer the shipment to a different owner

iii. Check the Shipment history

The other auto generated methods can also be used to test the chaincode.

i. Create a shipment with multiple products

The first step to test the chaincode is to create a shipment with multiple products. “CreateShipmentDetails” method is invoked to create the shipment details on-chain.

Since “ShipmentDetails” asset contains an array of “ProductDetails” embedded asset, the multiple sets of product details attributes can be added by clicking on the “+” button on the right side of “ProductDetails” embedded asset. 

ii. Transfer the shipment to different stakeholders

The second step to test the chaincode is to transfer the shipment ownership to a different owner. To transfer the shipment ownership, “TransferShipment” custom method is invoked. “TransferShipment” method takes ShipmentID and Owner as the inputs and updates the ShipmentID record with the new owner details.

iii. Check the Shipment history

The third step to test the chaincode is to get the complete shipment history with the ownership changes. “GetShipmentDetailsHistoryById” method accepts the ShipmentID as an input parameter and provides the complete shipment history details.

         The shipment history details returned from the ledger is as mentioned below -

[

  {

    "IsDelete": "false",

    "Timestamp": "2021-02-01 15:57:36.325 +0530 IST",

    "TxId": "6b7d09eb9dca22614dc0bac04c177c9ed7f9dee9393ff890cc895fed5f3a7a43",

    "Value": {

      "AssetType": "Shipment.ShipmentDetails",

      "Owner": "Manufacturer1",

      "ProductDetails": [

        {

          "Batch": [

            "B1",

            "B2",

            "B3"

          ],

          "ProductID": "P1",

          "ProductName": "Cookie 100 Gram",

          "ProductQuantity": 100

        },

        {

          "Batch": [

            "B10",

            "B11"

          ],

          "ProductID": "P2",

          "ProductName": "Butter 500 Gram",

          "ProductQuantity": 150

        },

        {

          "Batch": [

            "B101",

            "B102"

          ],

          "ProductID": "P3",

          "ProductName": "Milk 1 Litre",

          "ProductQuantity": 175

        }

      ],

      "ShipmentID": "S1"

    }

  },

  {

    "IsDelete": "false",

    "Timestamp": "2021-02-01 16:00:02.664 +0530 IST",

    "TxId": "8b21b0487d8f331b2a321dbde7ab8e82bbe3a7ebb673885788a35be493fc6eff",

    "Value": {

      "AssetType": "Shipment.ShipmentDetails",

      "Owner": "Distributor1",

      "ProductDetails": [

        {

          "Batch": [

            "B1",

            "B2",

            "B3"

          ],

          "ProductID": "P1",

          "ProductName": "Cookie 100 Gram",

          "ProductQuantity": 100

        },

        {

          "Batch": [

            "B10",

            "B11"

          ],

          "ProductID": "P2",

          "ProductName": "Butter 500 Gram",

          "ProductQuantity": 150

        },

        {

          "Batch": [

            "B101",

            "B102"

          ],

          "ProductID": "P3",

          "ProductName": "Milk 1 Litre",

          "ProductQuantity": 175

        }

      ],

      "ShipmentID": "S1"

    }

]

B.  Derived Key:

A chaincode asset requires a primary key attribute. Sometimes, it's useful to define a key that is not based on a single attribute, but derived from multiple other attributes. Frequently, a derived key is formed by concatenating multiple other attributes or a hash of concatenated values. For example, to create a chaincode asset that contains the students’ transcript details, the asset structure declares TranscriptID as the primary key for “StudentTranscriptDetails” derived by concatenating StudentID, SubjectID, CourseID and UniversityID attributes. Instead of using the composite key feature of Hyperledger Fabric, we are adding a new field that comprises concatenated attributes.

The implementation of derived key can be done in 3 easy steps –

  • Specification file design for derived key
  • Custom method to get record based on derived key formation
  • Testing of chaincode with derived key

1.  Specification File Declaration for Derived Key

The first step is to define a derived key in a specification file. The derived key TranscriptID is declared as the primary key and is formed by concatenating 4 attributes (i.e., StudentID, SubjectID, CourseID and UniversityID) with tilde symbol. This specific logic has been defined in the “format” properties in the specification file. By adding the “methods” section, App Builder will automatically generate the specified chaincode methods for the asset. In this case, createStudentTranscriptDetails, getStudentTranscriptDetailsById, updateStudentTranscriptDetails, and getStudentTranscriptDetailsHistoryById will be automatically generated. A custom method CustomGetterForTranscriptDetails has also been defined in the specification file to form the derived key to get the records.

The derived key can also be formed with hash value of concatenated attributes. Please refer to the derived property documentation for more details.

assets:

    - name: StudentTranscriptDetails

      properties:

          - name: TranscriptID

            type: string

            mandatory: true

            id: true

            derived:

              strategy: concat

              format: ["%1~%2~%3~%4","StudentID","SubjectID", "CourseID", "UniversityID"]

          - name: StudentID

            type: string

            mandatory: true   

          - name: SubjectID

            type: string

            mandatory: true

          - name: CourseID

            type: string

            mandatory: true

          - name: UniversityID

            type: string

            mandatory: true 

          - name: Year

            type: number

            mandatory: true

          - name: Semester

            type: string

            mandatory: true 

          - name: Grade

            type: string

            mandatory: true   

      methods:

          crud: [create, getById, update]

          others: [getHistoryById]

customMethods:

    - executeQuery

    - "CustomGetterForTranscriptDetails(StudentID string, SubjectID string, CourseID string, UniversityID string)"

2.  Custom Method to Get Record Based on Derived Key Formation

After scaffolding the project in Golang, the custom method (i.e., CustomGetterForTranscriptDetails) logic needs to be updated in controller.go file. The custom method will form the derived key and will pass the derived key value to the auto generated method GetStudentTranscriptDetailsById to fetch the specific record. The custom method takes 4 inputs – StudentID, SubjectID, CourseID and UniversityID to form the derived key. A sample custom code to form the derived key value and passing the value to the auto generated method has been shown below.

func (t *Controller) CustomGetterForTranscriptDetails(StudentID string, SubjectID string, CourseID string, UniversityID string) (interface{}, error) {

    var asset StudentTranscriptDetails

    asset.StudentID = StudentID

    asset.SubjectID = SubjectID

    asset.CourseID = CourseID

    asset.UniversityID = UniversityID

    id,err := model.GetId(&asset)

    if err !=nil {

        return nil, fmt.Errorf("error in getting ID %s", err.Error())

     }

    return t.GetStudentTranscriptDetailsById(id)

}

3.  Testing Chaincode with Derived Key

The chaincode can be tested once it’s installed and instantiated. The first step to test the chaincode with derived key will be to create a record. “CreateStudentTranscriptDetails” method is invoked to create the transcript details of the students on-chain. While creating the record through VS code extension, the derived key doesn’t need to be entered. The derived key will be automatically formed based on the logic mentioned in the “format” properties of the specification file.

The second step to test the chaincode with derived key will be to invoke the custom method “CustomGetterForTranscriptDetails” to get the specific record. Based on the values passed for the 4 attributes, the custom method forms the derived key value and fetches the specific key-value pair.

The record fetched from the ledger is as shown below -

{

  "AssetType": "TranscriptDetails.StudentTranscriptDetails",

  "TranscriptID": "S1~SUB1~C1~U1",

  "StudentID": "S1",

  "SubjectID": "SUB1",

  "CourseID": "C1",

  "UniversityID": "U1",

  "Year": 2020,

  "Semester": "2",

  "Grade": "A+"

}

The two new features - nested assets and derived keys - introduced in Blockchain App Builder 1.4.0 make it possible to auto generate more complex chaincodes for real world situations. The low code App Builder not only expedites the chaincode development by auto generation of chaincode but also helps make the maintenance of the chaincode easier. We believe that the low code development is essential to broader adoption of enterprise blockchain. So, there are lots of new features coming up in the future releases of Blockchain App Builder to make the chaincode development simple, enjoyable and more developer friendly. We welcome your questions / comments in the feedback section.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.