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.
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.
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
- Install PostgreSQL
- Create a database and a user of full privileges
- Run daml-on-sql to access the database
- Deploy Daml code
- Run Navigator
- Access Navigator
- Perform our scenario simulation
- 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.
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
psqlCREATE 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
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.
module BikeShop wheredata Currency = USD | EUR | GBP | CHF
deriving (Eq, Show)template Cash
controller owner can
Transfer : ContractId Cash
create this with owner=newOwnertemplate BikeRepair
signatory bikeShop, bikeOwner
controller bikeOwner can
Pay : ContractId Cash
cashCid: ContractId Cash
cash <- fetch cashCid
cash.currency == CHF &&
cash.amount == price)
exercise cashCid Transfer with newOwner=bikeShoptemplate BikeRepairProposal
controller receiver can
Accept : ContractId BikeRepair
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
Step 7: Scenario Simulation
We repeat the scenario as our previous articles. The summary of flow is like this.
- SwissBank issues Cash to Martin.
- BikeShop proposes Martin a service offer to fix his bike with the Cash on his hands.
- Martin accepts the proposal.
- 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.
For BikeShop, the Cash contract is now owned by BikeShop. All other repair contracts are archived as history.
For SwissBank, there is only one Cash contract, the outstanding one liable to BikeShop. No information about repair contracts is seen.
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.
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.