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 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:
- Standard entity, see How to create an Input Specification File.
- Entity represented as a NFT, see Input Specification File for Non-Fungible Tokens.
- Entity represented as a FT, see Input Specification File for Fungible Tokens.
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.
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).
- 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.
- Access the Oracle Blockchain Service Console to install and deploy the chaincode package into the founder instance.
- Navigate to the Chaincode tab, and click Deploy a New Chaincode.
- Select the Advanced Deployment option.
- 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.
- 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:
- 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.
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.
- Smart contract initialization (Init Admin User Accounts).
- Creation of wallets for users who can own the NFT tokens.
- Asign minter role for those users who should have that privilege.
AdminSteps
folder from the provided Postman collection.
- 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 }
- 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 }
- 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 }