X

Faster, Easier Development of Smart Contracts is Here Today with Blockchain App Builder for Oracle Blockchain Platform - Part 3

Todd Little
Oracle Blockchain Platform Chief Architect

In my last post I showed how to create, run, and test a smart contract using the Blockchain App Builder. In this post I’ll cover how we can take the newly developed smart contract and deploy and test it with an instance of the Oracle Blockchain Platform.

The major difference between running and testing a smart contract in the Oracle Blockchain Platform cloud service versus running it locally is that we can’t use things like nodemon to automatically restart and reload the chaincode container. Running locally, we use Docker to spin up the various Hyperledger Fabric components on the local system. When running in the Oracle Blockchain Platform, we must adhere to the Fabric chaincode lifecycle, meaning we need to upgrade the smart contract each time we make a change. Whereas running locally, saved changes are picked up immediately without having to upgrade the chaincode.

Another significant difference is that when running locally there is no identity management involved as we create local admin credentials that are used by the tools. In the cloud, one must be authenticated and granted permission to perform any operations.

Fortunately, the Blockchain App Builder hides much of this complexity by using mostly the same set of CLI commands as are used to run locally, just with some additional information required, such as the credentials and Fabric connection profile to use. To begin with, let’s get the connection profile from the Oracle Blockchain Platform. Navigate to the Developer Tooling tab in the Oracle Blockchain Platform console. From there select Application Development in the side panel. From there under OBP will be an option to download the development package. That development package is essentially a Fabric connection profile minus any credentials.

Once the development package has been downloaded, extract the files into a subdirectory of the project called obp. After extracting the files you should see a network.yaml file and a directory named artifacts in the obp directory. The network.yaml file is the connection profile and artifacts contains the crypto material needed to connect to an Oracle Blockchain Platform instance. However we still need to add the admin credentials for the instance to the crypto material. These can be downloaded from the OBP console by navigating to the network tab and then clicking on the hamburger menu associated with your instance. Select the Export Admin Credentials and download the zip file and unzip into the obp directory you created above in the project directory. It contains the public/private key pair for the admin user of the instance. From that directory, copy the files as follows:

obptools@ochain:~/obp$ mkdir -p artifacts/crypto/ordererOrganizations/tltest/signcert
obptools@ochain:~/obp$ mkdir -p artifacts/crypto/ordererOrganizations/tltest/keystore
obptools@ochain:~/obp$ cp tltest-cert.pem artifacts/crypto/ordererOrganizations/tltest/signcert/tltest-signcert.pem
obptools@ochain:~/obp$ cp tltest-key artifacts/crypto/ordererOrganizations/tltest/keystore/tltest-key.pem
obptools@ochain:~/obp$ mkdir -p artifacts/crypto/peerOrganizations/tltest/signcert
obptools@ochain:~/obp$ mkdir -p artifacts/crypto/peerOrganizations/tltest/keystore
obptools@ochain:~/obp$ cp tltest-cert.pem artifacts/crypto/peerOrganizations/tltest/signcert/tltest-signcert.pem
obptools@ochain:~/obp$ cp tltest-key artifacts/crypto/peerOrganizations/tltest/keystore/tltest-key.pem   

At this point we have all the information necessary to interact with the Oracle Blockchain Platform instance and can proceed with deploying, running, and testing the smart contract. The first step is to log into the Oracle Blockchain Platform instance as follows to get a new enrollment certificate:

obptools@ochain:~/fabcar$ ochain login -u todd.little@oracle.com
? Enter password [hidden]
[2020-10-20T15:33:46.042] [INFO] default - Login successful.
obptools@ochain:~/fabcar$

At this point we’re ready to deploy the chaincode using the ochain deploy command as follows:

obptools@ochain:~/fabcar$ ochain deploy --username todd.little@oracle.com
[2020-10-20T16:38:55.082] [INFO] default - 

============ Started Install Chaincode ============

[2020-10-20T16:38:55.469] [INFO] default - Chaincode fabcar:1 not installed.
[2020-10-20T16:38:56.043] [INFO] default - Successfully sent install Proposal and received ProposalResponse
[2020-10-20T16:38:56.043] [INFO] default - Successfully installed chaincode fabcar
[2020-10-20T16:38:56.043] [INFO] default - 

============ Finished Install Chaincode ============

[2020-10-20T16:38:56.044] [INFO] default - Successfully installed chaincode fabcar
[2020-10-20T16:38:56.045] [INFO] default - 

============ Started instantiate Chaincode  ============

[2020-10-20T16:40:42.399] [INFO] default - Successfully sent Proposal and received ProposalResponse
[2020-10-20T16:40:44.754] [INFO] default - The chaincode instantiate transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20009
[2020-10-20T16:40:44.754] [INFO] default - The chaincode instantiate transaction was valid.
[2020-10-20T16:40:44.771] [INFO] default - The chaincode instantiate transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20010
[2020-10-20T16:40:44.771] [INFO] default - The chaincode instantiate transaction was valid.
[2020-10-20T16:40:44.771] [INFO] default - Successfully sent transaction to the orderer.
[2020-10-20T16:40:44.772] [INFO] default - Successfully instantiated chaincode fabcar on channel default
[2020-10-20T16:40:44.772] [INFO] default - 

============ Finished instantiate Chaincode ============

[2020-10-20T16:40:44.772] [INFO] default - Successfully instantiated chaincode fabcar on channel default
obptools@ochain:~/fabcar$

If we now go to the Oracle Blockchain Platform service console, we can see that the chaincode has been installed and instantiated on our instance.

Deployed Chaincode

At this point we’re ready to try our new smart contract in the cloud. Again we’ll use the ochain invoke command to create a new car but we need to add the --obp-dev-package switch to indicate that the invoke should happen against a cloud instance and not the local Fabric instance we started before. So that looks like:

obptools@ochain:~/fabcar$ ochain invoke --obp-dev-package obp createCar '{"vin":"vinandserialnum01","make":"ford","model":"hybrid","year":2015,"color":"black","price":15000,"lastSold":"2020-09-01"}'
[2020-10-20T16:56:36.301] [INFO] default - 

============ Started Invoke Chaincode ============

[2020-10-20T16:56:36.749] [INFO] default - Successfully sent Proposal and received ProposalResponse
[2020-10-20T16:56:39.065] [INFO] default - The chaincode invoke transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20009
[2020-10-20T16:56:39.065] [INFO] default - The chaincode invoke transaction was valid.
[2020-10-20T16:56:39.067] [INFO] default - The chaincode invoke transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20010
[2020-10-20T16:56:39.067] [INFO] default - The chaincode invoke transaction was valid.
[2020-10-20T16:56:39.068] [INFO] default - Successfully sent transaction to the orderer.
[2020-10-20T16:56:39.068] [INFO] default - Successfully invoked method "createCar" on chaincode "fabcar" on channel "default"
[2020-10-20T16:56:39.068] [INFO] default - 

============ Finished Invoke Chaincode ============

[2020-10-20T16:56:39.068] [INFO] default - {"assetType":"fabcar.car","vin":"vinandserialnum01","make":"ford","model":"hybrid","year":2015,"color":"black","price":15000,"lastSold":"2020-09-01T00:00:00.000Z"}
obptools@ochain:~/fabcar$

We can use the Oracle Blockchain Platform service console to view the transaction by navigating to the Channels tab and clicking on the default channel. Then select the latest block with a data (user) transaction, and then click on the transaction below to expand the view of the transaction. This is what’s shown:

View Transaction

We’ve verified that the transaction was recorded in the cloud based ledger.

Next let’s look a little more at the differences between working against the local Fabric instance and the Oracle Blockchain Platform cloud-based instance. The local Fabric instance is running the chaincode in developer mode, meaning that we can control the lifecycle of the chaincode without having to go through the normal install and instantiate that’s needed in a regular Fabric instance. This allows using file watchers (nodemon and modd) that will automatically reload the chaincode whenever source changes are detected.

Another difference between the local and cloud instances is that the cloud instances don’t expose network ports for debuggers, whereas the local instance does. This allows using debuggers in your favorite IDE that can debug remote programs. With this capability it is possible to set breakpoints, inspect variables, and do line by line debugging on local instances.

Finally, the last significant difference is that there are features in the Oracle Blockchain Platform that aren’t present in standard Fabric such as SQL based rich queries. Thus, if a chaincode relies on SQL queries, it will have to be tested in an Oracle Blockchain Platform instance.

Let’s make a simple update to the chaincode and redeploy it to the cloud to demonstrate a mechanism to test your rich queries without continually update the chaincode. We’ll add another automatically generated function to the specification file that allows for the execution of arbitrary rich queries. By adding the executeQuery function to the customMethods section, Blockchain App Builder will automatically add an implementation of the function that allows making arbitrary dynamic queries. This is especially useful in testing complex SQL queries as they can be tried and tried without having to redeploy the chaincode. Here is what the end of the fabcar.yml should look like:

customMethods:
  - "buyCar(vin: string, buyer: string, price: number, date: Date)"
  - executeQuery

Since we’ve changed the specification file, we’ll need to merge that change in with our already generated code using the ochain sync command like:

obptools@ochain:~/fabcar$ ochain sync
INFO (SyncCommand): Initiating synchronization from spec file
    create .ochain.json
    create .vscode/launch.json
    create .vscode/tasks.json
    create README.md
    create .gitignore
    create .npmignore
    create package.json
    create lib/README.md
    create lib/chaincode.ts
    create lib/decorators.ts
    create lib/ochain-controller.ts
    create lib/ochain-model.ts
    create lib/utils.ts
    create main.ts
    create src/fabcar.controller.ts
    create src/fabcar.model.ts
    create tests/fabcar.spec.ts
    create tsconfig.json
Installing fabcar... /home/obptools/fabcar/.sync_temp/fabcar...ts
obptools@ochain:~/fabcar$ 

Under the covers a git repository was created and git merge was used to merge the new versions of the generated files with the edited ones. Fortunately there were no conflicts, but if there were, they would have to be resolved by hand.

The chaincode has been updated, so we need to deploy a new version of it to the cloud using the ochain deploy command. Blockchain App Builder will determine that the chaincode has been previously installed and instantiated, so it will perform a chaincode upgrade in the cloud instance. This may take a while as the chaincode has to be packaged, then installed on the peers, and finally instantiated, meaning a new Docker image is created and the chaincode compiled and run.

obptools@ochain:~/fabcar$ ochain deploy --username todd.little@oracle.com
[2020-10-21T05:08:37.715] [INFO] default - 

============ Started Install Chaincode ============

[2020-10-21T05:08:38.143] [INFO] default - Chaincode fabcar is installed, should run upgrade chaincode to version 2
[2020-10-21T05:08:38.807] [INFO] default - Successfully sent install Proposal and received ProposalResponse
[2020-10-21T05:08:38.807] [INFO] default - Successfully installed chaincode fabcar
[2020-10-21T05:08:38.807] [INFO] default - 

============ Finished Install Chaincode ============

[2020-10-21T05:08:38.807] [INFO] default - Successfully installed chaincode fabcar
[2020-10-21T05:08:38.807] [INFO] default - 

============ Started upgrade Chaincode  ============

[2020-10-21T05:10:47.049] [INFO] default - Successfully sent Proposal and received ProposalResponse
[2020-10-21T05:10:49.546] [INFO] default - The chaincode upgrade transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20009
[2020-10-21T05:10:49.546] [INFO] default - The chaincode upgrade transaction was valid.
[2020-10-21T05:10:49.550] [INFO] default - The chaincode upgrade transaction has been committed on peer tltest-oabcs1-iad.blockchain.ocp.oraclecloud.com:20010
[2020-10-21T05:10:49.551] [INFO] default - The chaincode upgrade transaction was valid.
[2020-10-21T05:10:49.551] [INFO] default - Successfully sent transaction to the orderer.
[2020-10-21T05:10:49.551] [INFO] default - Successfully upgraded chaincode fabcar on channel default
[2020-10-21T05:10:49.551] [INFO] default - 

============ Finished upgrade Chaincode ============

[2020-10-21T05:10:49.551] [INFO] default - Successfully upgraded chaincode fabcar on channel default
obptools@ochain:~/fabcar$

Let’s try out the new executeQuery function by querying for a count of the number of cars of each color each owner has:

obptools@ochain:~/fabcar$ ochain query --obp-dev-package obp executeQuery \
> 'SELECT 
> json_extract(t.valueJson, "$.owner") AS owner, 
> json_extract(t.valueJson, "$.color") AS color,
> COUNT(*) AS count 
> FROM  t WHERE json_extract(t.valueJson, "$.assetType") = "fabcar.car" 
> GROUP BY json_extract(t.valueJson, "$.owner"), json_extract(t.valueJson, "$.color") ORDER BY json_extract(t.valueJson, "$.owner")'

[2020-10-21T08:31:45.578] [INFO] default - 

============ Started Query Chaincode ============

[2020-10-21T08:31:46.016] [INFO] default - Successfully queried peer[0] on channel "default" using chaincode "fabcar"
[2020-10-21T08:31:46.016] [INFO] default - Successfully queried peer[1] on channel "default" using chaincode "fabcar"
[2020-10-21T08:31:46.016] [INFO] default - 

============ Finished Query Chaincode ============

[2020-10-21T08:31:46.016] [INFO] default - [{"color":"black","count":1,"owner":null},{"color":"silver","count":1,"owner":"dick@cars.com"},{"color":"black","count":1,"owner":"frank@gmail.com"},{"color":"grey","count":1,"owner":"frank@gmail.com"},{"color":"red","count":1,"owner":"frank@gmail.com"},{"color":"red","count":1,"owner":"harry@cars.com"},{"color":"silver","count":2,"owner":"harry@cars.com"},{"color":"black","count":1,"owner":"joe@oracle.com"},{"color":"black","count":1,"owner":"ralph@oracle.com"},{"color":"white","count":1,"owner":"ralph@oracle.com"},{"color":"black","count":2,"owner":"tom@cars.com"},{"color":"red","count":5,"owner":"tom@cars.com"}]
obptools@ochain:~/fabcar$

We can see that tom@cars.com has 2 black cars and 5 red cars. Let’s see number of cars owned by each owner and their average price ordered by the number of cars owned:

obptools@ochain:~/fabcar$ ochain query --obp-dev-package obp executeQuery \
> 'SELECT 
> json_extract(t.valueJson, "$.owner") AS owner,
> COUNT(*) as count,
> AVG(json_extract(t.valueJson, "$.price")) AS average
> FROM  t WHERE json_extract(t.valueJson, "$.assetType") = "fabcar.car" 
> GROUP BY json_extract(t.valueJson, "$.owner") ORDER BY count'
[2020-10-21T08:47:24.169] [INFO] default - 

============ Started Query Chaincode ============

[2020-10-21T08:47:24.521] [INFO] default - Successfully queried peer[0] on channel "default" using chaincode "fabcar"
[2020-10-21T08:47:24.521] [INFO] default - Successfully queried peer[1] on channel "default" using chaincode "fabcar"
[2020-10-21T08:47:24.521] [INFO] default - 

============ Finished Query Chaincode ============

[2020-10-21T08:47:24.521] [INFO] default - [{"average":15000,"count":1,"owner":null},{"average":19000,"count":1,"owner":"dick@cars.com"},{"average":14000,"count":1,"owner":"joe@oracle.com"},{"average":40250,"count":2,"owner":"ralph@oracle.com"},{"average":24733.333333333332,"count":3,"owner":"frank@gmail.com"},{"average":12233.333333333334,"count":3,"owner":"harry@cars.com"},{"average":17100,"count":7,"owner":"tom@cars.com"}]
obptools@ochain:~/fabcar$

Which shows us that Ralph owns 2 cars with an average price of 40,250, and Tom owns 7 cars with an average price of 17,100.

Using SQL queries instead of the standard Fabric CouchDB queries, one can push calculations down to the database eliminating the need to retrieve the assets and making the calculations in chaincode. The above query took one round-trip to the state database, meaning only one round-trip from the chaincode to the peer where queries are actually executed. Without the power of SELECT statements, the chaincode would have had to retrieve all the cars in the state database dramatically increasing the number of round-trips and/or the size of the messages.

Another benefit of using rich queries in the Oracle Blockchain Platform is that a hash of the query results is stored in the Read/Write set and validated at commit time. This ensures that any results from a rich query can be safely used in the chaincode without the risk of dirty reads, unlike standard Fabric.

In my next post I’ll introduce the Blockchain App Builder extension for Visual Studio Code. This extension provides all the capabilities of the CLI, but in the form of an extension that also provides line by line debugging, interactive merging of specification file changes into previously generated files, as well as lifecycle management of the blockchain network. See how the extension can speed up the development of your smart contract.

For more posts on Oracle Blockchain Platform, please visit our blog home.

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.