Fix my Bike again: Deploy Daml Project in PostgreSQL-based Daml Ledger

KC Tam
7 min readMar 16, 2021

Introduction

One feature that amazes me a lot in Daml is that we can work on the model (smart contract) first before we choose which and what type of ledger our model is deployed. This is how I arrange these several articles. The first article (link) focused on the code: modeling a real life example with Daml and testing it in Daml Studio. Everything happened inside the studio and no ledger was used yet. In the next article (link) we worked on a complete deployment, with Daml Sandbox as the ledger, and Navigator as the frontend. This was done through a single command.

As the name suggested, the ledger is not persistent in the Daml Sandbox. In real life deployment the model is always made in a persistent ledger, which can be a centralized database or a distributed ledger. Here in this article we demonstrate a ledger with a centralized database, deploying our “fix my bike” example to a PostgreSQL database. It is done through the daml-on-sql driver provided by Digital Asset.

Overview

Let us first revisit our previous article (link), in which we used one single command daml start. This command performs the following tasks

  • compile the project
  • bring up the Daml Sandbox as the ledger
  • deploy the compiled model to the ledger
  • bring up Navigator as the frontend

With this, we walked through our demonstration and simulated a scenario with Navigator.

In this article we use a PostgreSQL database as the ledger keeping everything for our project. A driver daml-on-sql is needed to expose the gRPC ledger API to the world. The daml-on-sql driver communicates with the PostgreSQL database through JDBC to the URL exposed in the PostgreSQL.

Here is how it looks like when the project is deployed in the PostgreSQL-based Daml ledger.

Note: This is a simplified version of architecture, showing those components of our interests in the demonstration. A more comprehensive architecture can be found in the documentation (link).

Note: Prior to SDK 1.8.0 PostgreSQL is an option in Daml Sandbox. It is no longer after SDK 1.8.0. Here (link) you can find more about the Daml driver for PostgreSQL, which is used in this demonstration.

Demonstration Setup

Both the PostgreSQL database and Daml SDK are in one host in our demonstration. PostgreSQL exposes the URL, and the daml-on-sql driver to interact through JDBC. The daml-on-sql driver now provides a standard Daml ledger API, and Navigator is accessing the ledger for demonstration.

Here is the flow

  1. Install PostgreSQL
  2. Create a database and a user of full privileges
  3. Run daml-on-sql to access the database
  4. Deploy Daml code
  5. Run Navigator
  6. Access Navigator
  7. Perform our scenario simulation
  8. Observe ledger persistence

In my setup I am using Vagrant and Virtualbox to launch a Ubuntu 18.04 host. Make sure you have arranged volume sharing (for daml-on-sql driver) and port forwarding (port 7500 to localhost) to make things work.

Demonstration

Step 1: Install PostgreSQL

Download and install PostgreSQL (link).

After successful installation PostgreSQL is running on my host.

Step 2: Create a database and a user

Here we are preparing a database damlledger and a user daml with password daml for the Daml ledger. The database will hold the ledger. And the user will be used by daml-on-sql. Note the user needs all privileges upon the database. We use psql to implement this.

sudo -i -u postgres
psql
CREATE DATABASE damlledger;CREATE USER daml WITH ENCRYPTED PASSWORD 'daml';GRANT ALL PRIVILEGES ON DATABASE damlledger TO daml;

We can see the database damlledger is newly created. The daml-on-sql driver will work upon this database later.

Step 3: Run daml-on-sql driver accessing the database

The daml-on-sql driver can be found in the release page (link). My demonstration is using SDK 1.11.0, and the file daml-on-sql-1.11.0.jar is inside Assets.

Here we need to specify the JDBC URL to our database damlledger in PostgreSQL, with the user and password.

java -jar daml-on-sql-1.11.0.jar --ledgerid=test --sql-backend-jdbcurl='jdbc:postgresql://localhost/damlledger?user=daml&password=daml'

By default, the daml-on-sql driver exposes localhost:6865 as the ledger API access point.

If we inspect the database damlledger again, we see the database is configured properly by the daml-on-sql driver.

Step 4: Deploy Daml project

It is time for our Daml project. We just use the same “fix my bike” model in my previous article (link) for this demonstration. We just repeat what we have done before.

daml new my-proj --template skeleton
cd my-proj

Inside the project file we modify the configuration file daml.yaml, and create the model file daml/BikeShop.daml. For easy reference they are posted here again.

daml.yaml

sdk-version: 1.10.0
name: bikedemo
source: daml
parties:
- Martin
- BikeShop
- SwissBank
version: 0.0.1
dependencies:
- daml-prim
- daml-stdlib
- daml-script
sandbox-options:
- --wall-clock-time

daml/BikeShop.daml

module BikeShop wheredata Currency = USD | EUR | GBP | CHF
deriving (Eq, Show)
template Cash
with
issuer: Party
owner: Party
currency: Currency
amount: Decimal
where
signatory issuer
controller owner can
Transfer : ContractId Cash
with
newOwner: Party
do
create this with owner=newOwner
template BikeRepair
with
bikeShop: Party
bikeOwner: Party
description: Text
price: Decimal
paymentDue: Date
where
signatory bikeShop, bikeOwner
controller bikeOwner can
Pay : ContractId Cash
with
cashCid: ContractId Cash
do
cash <- fetch cashCid
assert (
cash.currency == CHF &&
cash.amount == price)
exercise cashCid Transfer with newOwner=bikeShop
template BikeRepairProposal
with
proposer: Party
receiver: Party
proposal: BikeRepair
where
signatory proposer
controller receiver can
Accept : ContractId BikeRepair
do
create proposal

In contrast to our previous deployment, in which we used one single command daml start, here we need to break it into two commands. First we use daml deploy and specify the ledger access point (localhost:6865). This command performs several tasks: obtain parties from the configuration file, compile the model code and upload it to the ledger.

daml deploy --host localhost --port 6865

We see the deployment is successful. Our backend is ready and now we can work on the frontend.

Step 5: Run Navigator

In the previous article we use a single command daml start to bring up both the Daml Sandbox and Navigator. Now, to bring up the Navigator, we simply use daml navigator command. We specify port 7500 for our client (browser) access.

daml navigator server --port 7500

Step 6: Access Navigator

Open a browser and access the Navigator frontend. And we can see the roles are correctly set according to our configuration daml.yaml.

Step 7: Scenario Simulation

We repeat the scenario as our previous articles. The summary of flow is like this.

  1. SwissBank issues Cash to Martin.
  2. BikeShop proposes Martin a service offer to fix his bike with the Cash on his hands.
  3. Martin accepts the proposal.
  4. Upon satisfaction, Martin pays his Cash to BikeShop.

The resulting contracts of Martin, BikeShop and SwissBank are shown here.

For Martin, there are no active contracts. All contracts are archived as history.

In Martin’s view, all contracts are archived.

For BikeShop, the Cash contract is now owned by BikeShop. All other repair contracts are archived as history.

In BikeShop’s view, the Cash contract is active. All repair service contracts are history.

For SwissBank, there is only one Cash contract, the outstanding one liable to BikeShop. No information about repair contracts is seen.

In SwissBank’s view, there is still Cash contract, though the owner is now changed to BikeShop.

Step 8: Observe ledger persistence

So far we have the same results in our scenario. In fact we cannot tell from the frontend whether we are working in the Sandbox or in a Daml ledger with persistent storage. To observe how persistence works, we shutdown both the daml-on-sql driver and navigator (control-c), and halt the host.

Then bring up everything again: the host, the daml-on-sql driver and the navigator.

When we run the daml-on-sql driver, we notice the ledger id is found.

As we have no change on the model, we do not need to deploy the model again. Just bring up the Navigator.

Finally we will see all the previous contracts (active and archived) are still there, with the same contract IDs. For example, on the ledger view of SwissBank,

The ledger is persistent to the PostgreSQL database.

Summary

In this article we explored how to deploy our model in a PostgreSQL-based Daml ledger. Through this we see the command daml deploy for deploying our model to a ledger and daml navigator to bring up the Navigator frontend. Next time we will explore another setup where we deploy our model in other distributed ledger technologies.

--

--