Marlowe is specified by an executable semantics written in Haskell, but to make it usable in practice with financial contracts, it needs to be implemented on a blockchain. In this section, we explain how Marlowe is executed on the Cardano blockchain using an interpreterFootnote 1 written in the Plutus programming language.
3.1 Cardano and Plutus
Cardano is a third-generation blockchain that solves the energy usage issue by moving to an energy efficient Proof of Stake protocol
[2].
Cardano aims to support smart contracts during its Shelley release in 2020. Cardano smart contract platform is called Plutus, and it uses Haskell programming language to generate a form of \(System F_{\omega }\), called Plutus Core, by extending GHC using its plugin support
[8, Section 13.3].
To implement Marlowe contracts, we use the PlutusTx compiler, which compiles Haskell code into serialized Plutus Core code, to create a Cardano validator script that ensures the correct execution of the contract. This form of implementation relies on the extensions to the UTxO model described in
[6].
3.2 Extended UTxO
Cardano is a UTxO-based (unspent transaction output) blockchain, similar to Bitcoin
[5]. It extends the Bitcoin model by allowing transaction outputs to hold a Datum. As the name suggests, this is a serialised data value used to store and communicate a contract state. This allows us to create complex multi-transactional contracts. In a nutshell, the EUTxO model looks like this:
where black circles represent unspent transaction outputs, and red lines show transaction inputs that reference existing transaction outputs. Each transaction output contains a Value, and is protected either by a public key, or by a Validator.
In order to spend an existing transaction output protected by a Validator, one must create a transaction (a Context) that has an input that references the transaction output, and contains a Redeemer, such that Validator(Datum, Redeemer, Context) evaluates to True. A valid signature is required to spend a public key transaction output.
3.3 Design Space
There are several ways to implement Marlowe contracts on top of Plutus. We could write a Marlowe to Plutus compiler that would convert each Marlowe contract into a specific Plutus script. Instead, we chose to implement a Marlowe interpreter as a single Plutus script. This approach has a number of advantages:
-
It is simple: having a single Plutus script that implements all Marlowe contracts makes it easier to implement, review, and test what we have done.
-
Implementation is close to the semantics of Marlowe, as sketched above and in more detail in
[9], which makes it easier to validate.
-
The same implementation can be used for both on- and off-chain (wallet) execution of Marlowe code.
-
It facilitates client-side contract evaluation, where we reuse the same code to do contract execution emulation in an IDE, and compile it to WASM/JavaScript on the client side, e.g. in the Marlowe Playground.
-
Having a single interpreter for all (or a particular group of) Marlowe contracts allows us to monitor the blockchain for these contracts, if required.
-
Finally, Cardano nodes could potentially use an optimised interpreter (e.g: native) just for Marlowe contracts, which would save processing time.
Marlowe contract execution on the blockchain consists of a chain of transactions where, at each stage, the remaining contract and its state are passed through the Datum, and actions/inputs (i.e. choices and money deposits) are passed via the Redeemer. Each step in contract execution is a transaction that spends a Marlowe contract transaction output by providing a valid input as Redeemer, and produces a transaction output with a the remaining Marlowe contract and the updated state.
We store the remaining contract in the Datum, which makes it visible to everyone. This simplifies contract reflection and retrospection.
3.4 Contract Lifecycle on the Extended UTxO Model
As described above, the Marlowe interpreter is realised as a Validation script. We can divide the execution of a Marlowe Contract into two phases: creation and execution.
Creation. Contract creation is realised as a transaction with at least one script output, with the particular Marlowe contract in the Datum, and protected by the Marlowe validator script. Note that we do not place any restriction on the transaction inputs, which could use any other transaction outputs, including other scripts. This gives this model optimal flexibility and composability.
The contract has a state
where accounts maps account ids to their balances, choices stores user made choice values, boundValues stores evaluated Value’s introduced by Let expressions, and minSlot holds a minimal slot number that a contract has seen, to avoid ‘time travel to the past’.
Execution. Marlowe contract execution consists of a chain of transactions, where the remaining contract and state are passed through the Datum, and input actions (i.e. choices) are passed as redeemer scripts.
Each execution step is a transaction that spends a Marlowe contract transaction output by providing an expected input in a redeemer script, and produces a transaction output with a Marlowe contract as continuation.
The Marlowe interpreter first validates the current contract state: i.e. we check that the contract locks at least as much as specified by the contract balances (the accounts field in State), and that balances are strictly positive.Footnote 2
We then apply computeTransaction to the contract inputs, the contract continuation, and new state to compute the expected transaction outcomes:
where a TransactionInput consists of the current slot interval, together with other ontract inputs, and the outputs combine any payments and warnings with the resulting output state and contract.
Given a list of Input’s from Redeemer, the interpreter reduces a contract until it becomes quiescent: either it evaluates to Close, or it expects a user input in a When construct. All Pay, If, Let, Close constructs are evaluated immediately.
The evaluation function returns a new contract state, contract continuation, a list of warnings (such as partial payments), and a list of expected payments (i.e. one for each of the Pay constructs evaluated).
The on-chain Validator code cannot generate transaction outputs, but can only validate whatever a user provides in a transaction. Consider this simple zero coupon bond example.
Here we expect Alice to deposit 850 Ada (850,000,000 Lovelace) into her account aliceAccount before slot 100. Otherwise, we Close the contract.
If Alice deposits the money before slot 100, money immediately goes to Bob, by requiring a transaction output of 850 Ada to Bob’s public key address. Alice must produce the following Redeemer to satisfy the Marlowe validator:
Bob is then expected to deposit 1000 Ada into Alice’s account before slot 200. If he does, the contract is closed, and all remaining balances must be paid out to their respective owners; in this case, 1000 Ada must be paid to Alice. If Bob does not pay, then Alice has lost her money, because this is an unsecured loan.
Note, that it is possible to provide multiple inputs at a time, allowing as many steps of a contract execution as necessary to be merged. This gives atomicity to some operations, and saves on transaction fees.
Ensuring Execution Validity. Except for the transaction that closes a Marlowe contract, the Marlowe validator script checks that a spending transaction contains a valid continuation output, i.e: the hash of the output validator is the same (same hash), and the new state and contract are the expected ones: the ones resulting from applying the computeTransaction to the given inputs.
Closing a Contract. When a contract evaluates to Close, all remaining balances the accounts of the contract are payed out to the respective owners of each account, and the contract is removed from the set of unspent transaction outputs.
Future Work. Cardano extends its ledger rules to support forging of custom currencies and tokens. Simple token creation gives interesting possibilities of representing Marlowe contract parties by tokens. This tokenization of contract participants abstracts away concrete public keys into contract roles. In turn, those roles could be traded independently of a contract. We are working on adding multicurrency or roles support to Marlowe.