Building an application specific blockchain using Cosmos SDK Part-4

Harish Bhawnani
Coinmonks
13 min readApr 7, 2022

--

Photo by Launchpresso on Unsplash

In part-3 we finished handling the query handlers and store keepers for contract and deals. Let’s now tackle the Msg server.

Run the command to create the createDeal message-

This command does the following -

  • Creates the message definition (tx.proto).
  • Implements methods to satisfy the sdk.Msg interface (message_create_contract.go).
  • Creates deal message handler (msg_server_create_deal.go).
  • Generates the cli command to invoke the handler (tx_create_deal.go).
  • Registers the handler (handler.go).

Let us modify the message handler msg_server_create_deal.go as shown below -

Observe, at the end of execution we are consuming certain amount of gas. This is to incentivise the validators and discourage the spam Txs. We have defined gas units under constants (contract_utils.go).We are also emitting a custom event to inform the clients about details of successful Tx.

Note that in logic handling part, we first fetch the dealCounter to get the dealId and then save the deal in store. Also before saving deal we are validating the message with the help of Validate() method which will validate the vendor address and commission -

Lets code for the cli command tx_create_deal.go -

Note that method msg.ValidateBasic() (as used above) also gets invoked by cosmos sdk in checkTx method to validate the Tx so that tendermint can avoid invalid Txs in mempool.

Now, let us scaffold the createContract command -

Our proto definitions would look like -

The contractStatus field has been added later manually. After adding the field contractStatus field, run the command to regenerate proto files-

Modify the handler as below- (msg_server_create_contract.go)

We are storing the contract against the key- NewContract/value/{contractId}/ under the store with prefix key NewContract/value/{dealId}/. Also, we validate the createContract Txs can be processed only if tx originates from the deal owner.

Once contract has been created by dealOwner, Vendor needs to commit & consumer needs to approve the same before expiry time or else contract would be considered as expired. To do so we will generate two more messages- commitContract and approveContract.The tx commitContract will input the shippingTime (vendorETA) in mins which is required as a commitment of order completion from vendor side. Scaffolding these two messages is left upto you as an exercise :)

Let’s take a look at the commitContract handler -

Under commitContract we validate the following before changing the contract status to committed -

  • If the transaction is coming from vendor.
  • If the contract hasn’t been expired.
  • If vendorETA ≤ ownerETA /2.

Let take a look at approveContract handler -

The approveContract tx will be initiated by consumer . This tx will perform the following steps -

  • Validate if the tx is signed by consumer.
  • Validate if the contract has been committed.
  • Validate if the contract hasn’t been expired.
  • Transfer funds from consumer’s account to module escrow account.

Note that the definition for msg.DealHandlerValidation used in handler is as given below -

We are using bank keeper here for fund transfer operations. In order to access any of the capability exposed by bank keeper we need to inject bank keeper interface in our concrete keeper type -

Therefore our keeper constructor would change as below -

Lets pass the bank keeper interface in our keeper construction during initialisation phase in app.go

As we are passing bank keeper interface which is not of concrete type, our keeper concrete type needs to understand which of the method from bank keeper interface would be used.Therefore define an interface under expected_keepers.go for methods we are going to access from bank keeper -

Note that we will also be using Account keeper in orderDelivered handler. Therefore pass the same in our concrete keeper type, exactly the same way as we did for bank keeper interface.

After the contract has been approved, we must emit the custom event to let the FE client know about the contract status. This will inform the vendor about contract approval so that vendor can start processing the order. Once the order is ready, vendor will initiate a tx to change the contract status to INDELIVERY. This tx will also calculate if there is any shipping delay based on the initial shipping commit time (vendorETA). Let’s take a look at handler -

Here we are recording the shipping delay if any, before changing the contract status to INDELIVERY. This will help us to calculate the delay charges once the order has been delivered. Note that we have current time available from ctx.BlockTime(). Based on delay charges, payments will be calculated for dealOwner and vendor. And delay charges if any, will be refunded back to the consumer account.

Once the order reaches the consumer doorstep, he needs to initiate a tx for order completion. This tx will mark the order as completed and will settle all the funds based on vendor commission and delay charges. Let’s take a brief look into orderDelivered handler.

In orderDelivered handler above , with the help of order starting time and shipping delay we calculate the delivery delay if there is any. The calculated delay charge is aggregated with shipping delay charge and refunded back to the consumer. Whereas vendor and owner are paid according to the vendor commission and delay charges.

Let’s add a handler to cancel the order, in case order delivery takes more time, user might want to cancel the order.

In case order delivery is delayed by 20 minutes, we allow user to cancel the order and refund back the complete amount to user account address.

Finally we are done with Msg server handlers. Do verify that our msg handler has been registered by the module -

Now before starting blockchain and testing out the messages and queries, let us rectify the genesis state to initialise the deal counter value.

Make sure we initialise and export the genesis state properly -

Run the command starport chain serve to spin up our development node.

Open another terminal & run the command starport chain build , this will generate deald binary which will be used to test txs and query against the running node.

In next part we will learn about the custom event indexing and how a user can subscribe to custom indexed events.

--

--