Create and Deploy the Smart Contract

For creating the smart contract, we need to define the entities to be stored and managed in Blockchain as shown in the entity model of the following image.


Description of oracle-blockchain-cms-entity.png follows
Description of the illustration oracle-blockchain-cms-entity.png

All these entities and their relations, all the logic required to manage and interact with the entities, and also to persist them into the blockchain ledger is defined in the smart contract.

The folder is represented as an NFT token, so it will be developed and initialized as an NFT. The other dependent entities (Documents and Properties), are standard entities and are child entities of the folder, so will be developed as standard entities without the need for initialization.

Once the smart contract is created, we will install and deploy it in the Blockchain Network we have created.

Create the Smart Contract

Once you have configured the Oracle Blockchain App Builder, you can create the specification file. The specification file can be created as a simple YAML file shown in the following code.

Note:

You can also download this file from GitHub: Download YAML file.
#
# Token asset to manage the complete lifecycle of a non-fungible token representing a folder to hold docuements. 
# This specification file will generate an Smartcontract project with a non-fungible token for the folders to be maintained by the users.
#
assets:
    - name: folderNFT
      type: token
      symbol: eDocs
      standard: erc721+
      anatomy: 
        type: nonfungible
        unit: whole
      behavior:
        - indivisible
        - singleton
        - mintable:
        - transferable
        - burnable
        - roles:
            minter_role_name: minter
      properties:
          - name: folderHASH
            type: string
          - name: documents
            type: document[]
      metadata:
          - name: folderID
            type: string
            mandatory: true
            id: true
          - name: folderType
            type: string
            mandatory: true
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
    - name: document
      properties:
        - name: docName
          type: string
          mandatory: true
          id: true
        - name: docURL
          type: string
          mandatory: true
        - name: docHASH
          type: string
          mandatory: true
        - name: docType
          type: string
          mandatory: true
        - name: docProperties
          type: docProperty[]
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
    - name: docProperty
      type: embedded
      properties:
          - name: propName
            type: string
            mandatory: true
          - name: propValue
            type: string
            mandatory: true
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
customMethods:
    - executeQuery
    - "attachDocument(tokenId: string, docName: string, docURL: string, docHASH: string, docType: string, docProps: string[], docVals: string[])" # Attach a document to an existing folder. 
    - "retrieveDocuments(tokenId: string)" # Retrieve Documents of an folder. 
    - "transferFolder(tokenId: string, fromOrgId: string, fromUserId: string, toOrgId: string, toUserId: string)" # Transfer the folder among participants. 
    - "updateFolderHASH(tokenId: string, newHash: string)" # Update HASH folder 
    - "getFolderHASH(tokenId: string)" # Check HASH folder 

In this specification file, in the first entity defined (folderNFT) you can see all the sections and attributes for the representation of a NFT token. Overview of the sections defined in the file:

  • Assets: Place where the different assets (standard entities, FTs, NFTs) are defined. Inside each of the assets we can distinguish different sections which can vary depending on the kind of represented asset. For NFTs and FTs these are the different subsections:
    • Type/Symbol/Standard: Specify that this token is based on the ERC-721 Standard, and give it an unique symbol indentifier.
    • Anatomy: Specify it is a NFT and whether it is subdivided into smaller fractions (nowadays "whole" is the only option for NFT tokens).
    • Behavior: Defines if the token can be minted, and in such case, what is the maximum number of mintable tokens. Here you must also state it is an indivisible token, if is singleton for each class, transferable, and burnable which is similar to its deletion (but not disappearing, so it is still there but not usable at all). Also in this section you can restrict token behavior to specific roles.
    • Metadata: Defines a sort of properties which must be set during token creation, and cannot be changed in the future. So its value will remain immutable for the whole life of the token.
    • Properties: Defines standard attributes of the token which can vary during the life of the token, like the array of documents which compose the folder.
  • customMethods: Location where the list of our custom methods must be defined. For those methods, Oracle Blockchain App Builder will only generate the signature of the method, without any implementation on them. The implementation of these methods are the only code that can be implemented by the developer.

The following links describe how to configure any kind of entity (NFT, FT, or standard entities) based on your business needs:

Once the specification file has been created, we can mandate Oracle Blockchain App Builder to create the scaffold of the project by following these next steps.

  1. If the specification file has been created outside of the Oracle Blockchain App Builder, you must import the specification file into Oracle Blockchain App Builder. Click on the ellipsis next to the SPECIFICATIONS frame, and from the pop-up, click Import Specification.
  2. Select the specification file, and click Import Specification.
  3. To be able to create a new chaincode project based on the imported specification file, click the + icon on the upper-right corner of the CHAINCODES frame. It will open the Create Chaincode wizard.
  4. In the Create Chaincode wizard, you must specify the following details:
    • Name: Name for the project.
    • Language: Language to create the scaffold of the project. You can only select between Typescript and GoLang. As we are using Typescript in this solution playbook for the implementation of the custom methods, we recommend selecting Typescript.
    • Specification: Select the specification file we just created in the previous steps.
    • Location/Domain: Depending on the selected language, you will be prompted to define the location where the project will be placed for the Typescript language, or the domain for the GoLang language.
    If all details are correct, we should see a green message in the wizard. If this is not the case, check the output generated during the creation of the scaffold of the project.
  5. In the CHAINCODES section, we should see the new generated project. Clicking in the project, navigate to the src folder inside the project, where the source code has been generated:
    • Inside the src/controller folder, we will see the main class representing our smart contract, with all the autogenerated methods to manage our entities.
    • Inside the src/model folder, we will see the class representing the NFT entity.
  6. Navigate to the src/controller folder, and select the controller class, we will see all the code auto-generated by Oracle Blockchain App Builder, and going to the end of the file, we will see all the signatures of the custom methods without any implementation on them.
  7. At this point we should create the implementation of the custom methods. For simplicity, we have provided all the implementation code for such methods in the file CMS_customMethods.ts available in GitHub. You only need to substitute the signatures autogenerated by Oracle Blockchain App Builder, with the content in the referenced file.
The Controller class, lines before the custom methods, includes all the auto-generated code to manage the lifecycle of the NFT tokens. The following image depicts the different areas covered by such methods.

oracle-blockchain-nft-token-oracle.zip

At this point the chaincode is ready to be used, so we can deploy and test the chaincode locally by following the instructions from Test Your Chaincode on a Local Hyperledger Fabric Network.

Deploy the Smart Contract

Once we have locally tested the chaincode, proceed to deploy it in the real network previously created using the Oracle Blockchain Service Console by:

  • Packaging the chaincode project.
  • Installing and Deploying the chaincode package into the single instance (founder).
The following section describes the detailed steps to execute the deployment.
  1. Create the deployable package from the chaincode project. From Visual Studio, click the right button on top of the chaincode project name, from the pop-up menu select the Package option, and select the directory to save the chaincode package file.
  2. Access the Oracle Blockchain Service Console to install and deploy the chaincode package into the founder instance.
  3. Navigate to the Chaincode tab, and click Deploy a New Chaincode.
  4. Select the Advanced Deployment option.
  5. Set all the values to install the chaincode package into the founder instance and click Next.
    • Package Label: Give a name which can help you identify which package is installed in the different existing channels. As you can have more than one version of the same smart contract deployed in different channels, it is a good practice to set a package label name like:
      <smartContractName>_<channel>_<version>
      
    • Chaincode Language: Select among the different languages, based on the language in which you have developed the chaincode.
    • Target Peers: Select the peers in which you want to install the chaincode package.
    • Is Packaged Chaincode: Leave this checkbox unselected if you are uploading a zip file. Select the checkbox for tar.gz files.
    • Chaincode Source: Click Upload Chaincode File and select the chaincode zip file.
  6. If the installation succeeds, we will see the success message. Next step is deployment of the chaincode in the selected channel, so you must set all values related with the deployment phase and click Next.
    • Channel: Select the channel in which you want to deploy the smart contract.
    • Chaincode Name: Set the name with which the smart contract will be deployed on the channel.
    • Version: Asign a number to this deployment, which is aligned with the package installed earlier. In this way, you will be able to correlate packages installed with chaincodes deployed in different channels.
    • Init-required: Select this checkbox if the init method of the chaincode needs to be invoked before allowing user transactions.
    • Endorsement Policy: Specify endorsment policies during deployment. In this solution playbook example, we are not using endorsement policies.
    • Private Data Collection: Set Private Data Collections if required. In this solution playbook example, we will not set the private data collection.
If the deployment succeeds, after closing the installation and deployment, you should see how the package has been installed in the two peers of the instance, and also instantiated in one of the channels.

Initialize the Smart Contract

When you are dealing with FTs and NFT tokens, there is a set of administrative actions to be executed before being able to execute your business methods. With Oracle Blockchain, all the administrative tasks can be executed as simple REST calls and it considerably reduces effort needed to initialize the smart contract.

Note:

Before executing any of the following steps, we must create the enrollmentIDs into the REST Proxies for those users granted access to the smart contract methods. The enrollment is a mapping between the username who invokes the REST API, and the accounts managed internally by blockchain to which tokens will be assigned as described in the topic Create enrollments in the REST Proxy nodes.

Use the following Postman collection to easily initialize the smart contract: Download Postman collection

In the AdminSteps folder of this Postman collection, there are three request calls to be executed for the smart contract initialization.

The Postman collection is ready to be used, but there is a set of variables which needs to be configured adequated to your own environment (passwords, URLs, and so on). These variables are set in the Variables tab of the Postman collection. The following table shows all the variables we have defined and needs to be adapted to your environment.

Variable Name Variable value
bc_founder_provider_url https://org1-w....
bc_timeout 60000
bc_nft_founder_userid1 cmsleg001
bc_nft_founder_userid1_pwd password
bc_nft_founder_userid2 cmsfin001
bc_nft_founder_userid2_pwd password
bc_nft_founder_userid3 cmsrsk001
bc_nft_founder_userid4_pwd password
bc_channel_name wedocms
bc_chaincode_name WEDOCMS

Find the endpoint in which the REST API is accessible from the Blockchain Service Console.

  1. Access the Blockchain Service Console through the OCI Console.
  2. Go to the Nodes tab. It will show you all the nodes which compose this instance, and in the restproxy node you will see the endpoint URL in the Route column.
    Remember that enrollments are created at instance level, not network level, so the enrollment for user will be only available through the restproxy URL of the founder instance, so, if new instances join the network, with new users allowed to access to the network, those users will need to exist in the proper tenancy, and the enrollments for those users should also be created in the restproxy of the corresponding instance.
Once the Postman collection has been properly configured, we can proceed with the smart contract initialization. The initialization of an NFT smart contract is considerably simpler than the initialization of a FT smart contract, we just need to execute three steps:
  1. Smart contract initialization (Init Admin User Accounts).
  2. Creation of wallets for users who can own the NFT tokens.
  3. Asign minter role for those users who should have that privilege.
The following API REST calls correspond to the calls into the AdminSteps folder from the provided Postman collection.
  1. Initialize the chaincode (Step-0: Init Admin User Account) indicating which user accounts will be allowed to execute administrative tasks. It is important to set the args of the init method correctly: args: Scaped array of user_ids with their org_ids.
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}",
        "args": [
            "init",
            "[{\"orgId\":\"org1\",\"userId\":\"cmsleg001\"},{\"orgId\":\"org1\",\"userId\":\"cmsfin001\"},{\"orgId\":\"org1\",\"userId\":\"cmsrsk001\"}]"
        ],
        "timeout": {{bc_timeout}},
        "isInit": true,
        "sync": true
    }
    
  2. Create user accounts for all users who can be custodians of the NFT assets representing the physical assets. It can be done by executing the Step-1: Create account Postman request. For the specifics of our use case, there are only three users related with the same single organization belonging to the network. This call must be executed as many times as users for which we want to create an account. In our case three times, each with following parameters:
    • "createAccount", "org1", "cmsleg001", "nonfungible"
    • "createAccount", "org1", "cmsfin001", "nonfungible"
    • "createAccount", "org1", "cmsrsk001", "nonfungible"
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}", //Smartcontract name
        "args": [
            "createAccount", "org1","cmsleg001","nonfungible" //Method, OrgID, UserID, fungible for FT / nonfungible for NFT
        ],
        "timeout": 60000,
        "sync": true
    }
    
  3. Set which user is allowed to mint tokens, in this case mint a token means create a new folder to hold a new set of documents, so you can decide which of the three existing users (cmsleg001, cmsfin001, or cmsrsk001) can execute these actions, and for those users execute the request Step-2: AddRole from the Postman collection.
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}", //Smartcontract name
        "args": [
            "addRole", //Method name
            "minter","org1","cmsleg001" //Role, OrgId, UserID
            ],
        "timeout": 60000,
        "sync": true
    }