Booking a taxi ride on distributed ledgers: A smart contract example (Part I)
Have you ever been in your favorite city, needing to call a taxi and having first to install an application on your smartphone in addition to the 10 previously installed taxi apps? What if you could use a single application wherever you go to book your rides regardless of whether it is a taxi, public transport ride, or e-scooter?
In this post, we explain how we can use Daml and a distributed ledger such as Canton (canton.io) to write such an app that is beneficial for both the service providers (transport operators), the travelers, and the drivers. We will first discuss the design and implementation in Daml and then see that without changes to the code, we can deploy the application to run on a decentralized ledger in a scalable, resilient, and open way.
An informal description of the problem
In order to give a brief overview of the problem, let’s consider the situation from the perspective of a traveler—let’s call her Alice—who wants a ride from the main train station to her office. When specifying her ride request, Alice gives her pickup location (the train station), drop off location (the office), and the maximum time she agrees to wait before being picked up. She wants the pickup to happen as soon as possible and to spend as little time as possible inside the vehicle.
A simplified, positive path is as follows:
- Alice sends a ride request to the operator (transportation provider)
- The transport provider collects the state of the vehicles (current location and planned actions) and makes a ride proposal (with a specific vehicle assigned)
- Alice accepts the ride proposal
- A new mission (with Alice’s pickup and drop off locations) is sent to the vehicle
The above flow can fail if no vehicle can reach Alice’s pickup location early enough (within her specified maximum waiting time) or if she rejects the ride proposal.
We may think from the above figure that the operator is the central entity governing the ride-booking process. However, we will see below that the operator is one party among others, together with the vehicles, the traveler, and the bank, and that multiple operators can be involved.
Benefits of a decentralized application
We present here some of the benefits of implementing a decentralized application deployed with a synchronization protocol we call Canton. We also bring in some features that come for free thanks to the Daml language. More details about the topology can be found in the Distributed setting section below.
On the operational side, vehicles can seamlessly connect to different nodes running the application, which allows to balance the load (e.g., routing computations) and increase scalability. Moreover, the ability to connect a vehicle to different nodes also improves the resiliency of the system.
On the traveler side, it is possible to connect to several providers, thus getting a unified view containing offers from the different providers. This behavior is similar to what an aggregator would provide.
For the drivers, the ability to connect to different nodes means they can belong to different fleets at the same time, and are thus able to propose rides to different operators, unlike in most current systems.
The atomicity of our protocol also enables transactions that span multiple vehicles: booking two legs of a journey (e.g., round trip), removing a ride from one vehicle and assigning it to another, and so on. The ability to handle such transactional atomicly is very important (e.g., to avoid the situation where only part of a round trip is confirmed or canceled) and is notoriously difficult in a distributed system. Such flows are enabled without the need for the programmer to actually focus on these aspects.
Finally, it is worth mentioning that the authentication of the different actors (including the drivers/vehicles) is handled easily with Daml. In addition, the privacy of the different views (vehicles plans, traveler rides, bank transactions, etc.) results directly from specific features of our language. We’ll discuss these in the next section.
In this section, we describe the implementation of the application in Daml. We first present the different actors and the parties involved and discuss how the state is modeled. In the next post, we will demonstrate the different steps in the booking flow. Finally, we briefly explain what additional pieces are required in order to simulate such a system on a local machine.
Actors, parties, and state
Looking back at the figure depicting the simple flow above, we see that the following actors are involved: a traveler (Alice), a few vehicles, and an operator (or transportation provider). Let’s consider the different actors separately.
Each traveler is modeled as a Daml party and can request rides.
Vehicles and their properties
For a given vehicle, we separate its state into two parts: an immutable one and a mutable one. The immutable one contains properties that do not change over time and can include seating capacity, maximal speed, and certain other characteristics (fuel vs. electric, energy profile, license plate, etc). For the sake of simplicity, we consider in this example only the speed, and we can thus write the vehicle properties as the following template:
As we can see, in addition to the actual vehicle properties (here, the speed), we also associate the operator and a vehicle identifier, which contains both information about the driver as well as a name for the vehicle. In terms of authority and privacy, the operator is the sole signatory on the contract and the driver is added as an observer.
On the other hand, the mutable part consists of the last known location of the vehicle (space and time) as well as information about activities it has to perform (pickup and dropoff of travelers):
The list of stakeholders of this template is exactly the same as for the vehicle properties one.
The separation between properties and state (immutable and mutable) provides the ability to keep contract instances as small as possible, which is useful, since the vehicle contract is frequently “updated” (each time a ride is added or removed from the vehicle). For a similar reason, some updates of the vehicle state (last known location, dropping finished activities) is done only when needed, and not eagerly.
Unsurprisingly, the last piece is the fleet, which contains the list of all vehicles managed by the operator:
Again, we notice that the fleet is mostly immutable: it needs to be updated only when a vehicle is added or removed to the fleet, not when the state of a vehicle changes.
Each driver is represented by a party and associated with a vehicle. Note that we could also directly represent vehicles by parties. This would also make sense, especially if the fleet consists of autonomous vehicles.
In addition to the parties described before, we also consider a bank with which travelers hold bank accounts. These accounts can be used to wire money and pay for the ride. In the description of the flows below, we will mostly ignore this part. In practice, we would instead integrate with a payment gateway.
In the next post, we’ll look at the booking flow and how Canton enables the deployment and allows for the connection and synchronization of different ledger instances while still maintaining privacy and integrity.