# I tested Elements — Simplicity

Simplicity aims to go beyond the limits of Bitcoin scripting and allows to implement complex smart contracts on Bitcoin and Bitcoin-like blockchain (Elements, Liquid). We will see here the points that will allow you to understand how the language works.

The Simplicity’s technical references: https://github.com/ElementsProject/simplicity/blob/pdf/Simplicity-TR.pdf

Simplicity is a language aiming to be used through another programming language. For now, there are two implementations:

• The formal implementation with Coq.
• A more accessible implementation in Haskell.

Both languages are functional. The functional programming paradigm is based on the lambda calculus. It means that all the computations are represented with pure functions. This property makes the mathematical verification of expressions much simpler. Verifying a pure function consists only in evaluating its output for a given input.

Back to Simplicity, We’ll use the Haskell library. Haskell is a very powerful functional language. If you want to learn it, there is the following very good course online: http://learnyouahaskell.com/chapters. It is necessary to understand Haskell’s concepts and syntax to follow this article.

The article is organized into two parts:

1. Simplicity code source, describe how the language works and investigates the Haskell implementation.
2. Simplicity first transaction: a more practical part that lets you reproduce the official first transaction by Russel O’Connor.

This article was written with the help of Nicolas Cantu. Thanks also to Russel’O’Connor for answering questions and sharing the transcript of the first transaction.

# Simplicity source code

The first part of the article focuses on how the Haskell implementation of simplicity works. It helps to understand the language. The simplicity Haskell library source code is available here: https://github.com/ElementsProject/simplicity/tree/master/Haskell

If you want to explore the code, clone the simplicity repository, and go inside the Haskell folder:

## Introduction to Simplicity

Simplicity is a typed functional programming language. As a functional language, it consists of applying a combination of expressions. These expressions are created from 9 combinators.

Simplicity scripts work as a Bitcoin script. It is a stack-based programming language. It means that the program manipulates a stack data structure.

The expressions applied to the stack are low-level expressions that modify the state of the stack. This has several advantages, including being easily verifiable and efficient.

The way that users can call simplicity scripts is very similar to a P2SH (pay to script hash). P2SH means that users do not commit the funds directly to the script but rather they commit to a hash of their Bitcoin script. The script is revealed only when the funds are redeemed. The protocol handles the script hash verification.

Simplicity works in much the same way except that it is not a linear hash of the script that is committed but the Merkle root of the script. All the operations (= the combinators) that compose the scripts form a Merkle tree. Then the root of the tree is committed on the blockchain.

## Simplicity Core

The Simplicity Core gathers all the common features. The core is independent of what blockchain Simplicity runs.

## Types

Simplicity language uses three kind types:

• The unit type `1`.
• The sum of two Simplicity types, `A+B` .
• The product of two Simplicity types, `A*B` .

The Haskell library implements them with several Haskell design patterns. Let’s see how it works.

First of all, there is `TyC` class with three instances:

This class is using to bind the authorized Haskell types to Simplicity types. We called this a constraint. In our case `TyC` constraints us to use the following Haskell types:

• The empty tuple type `()` : a special Haskell type which has a unique value:`()` . Often used to represent Unit.
• The `Either a b` type, where `a` and `b` are types of class `TyC` . Either documentation.
• The pair type `(a, b)` , where `a` and `b` are types of class `TyC` .

Thus, `1` which is an `Int` hasn’t an instance of the `TyC` . Same thing for the following expressions:

Actually, the types allowed are empty tuples compositions. (Or compositions of empty tuples compositions).

⚠ It could be a bit confusing when we deal with empty tuples`()` . The `()`is both a type (type for empty tuples) and the value of an empty tuple.

Note that the `TyC` type class is build using a pattern so that its methods are not exported. Thus, anyone will not be able to create new instances of the class.

As you can see from the last examples, manipulating the empty tuples compositions is not very comfortable. The data-level type `TyReflect` will solve the problem and link all of that with the Simplicity types.

This is a Generalized Algebraic DataType (GADT). Which implements all the Simplicity types with three data constructors:

• The Simplicity Unit type: `OneR` takes no argument and returns an empty tuple.
• All the types resulting from the sum of two other Simplicity types : `SumR` takes two `TyReflect` ( `a` and `b` ) and returns `Either a b` .
• All the types resulting from the product of two other Simplicity types : `SumR` takes two `TyReflect` ( `a` and `b` ) and returns `(a, b)`.

Now we can rewrite the previous examples with our data types:

For each type constrained by the `TyC` class, there is a `TyReflect` GADT associated.

The function `reify` returns the unique `TyReflect` value corresponding to the `TyC` type given as parameter.

The last element used to capture Simplicity type is the type alias `Ty` .

When we’ll write our smart contract, we cannot use the classic Haskell type like Bool, String, or whatever. All our data need to be compliant with the simplicity type.

Fortunately, with the three simplicity types, we can implement everything and so create our data type.

We can, for example, represent a single Bit:

And canonically map it to the Haskell type `Bool` :

With Bit representation, we are now able to represent everything. Blockstream developers have provided a `Word.hs` file that shows you how to represent a Word (= a Bits vector) with the Bit type.

## Terms and combinators

Now we have seen what are Simplicity types and how they work. Let’s see how we can manipulate these types with combinators. The Haskell implementations of Simplicity’s combinators are located in the `Core.hs` file.

Identity

Return the identity function for the type given as a parameter.

Composition

Takes two terms as parameters and returns the composition of these two terms.

Constant Unit

This one is simple, it always returns `()` whatever the type or the value of the argument.

Left Injection

We take a term `t` applied to `a` and compose it with the Left constructor of the Either data type.

Right Injection

The same thing that the Left injection but use the Right constructor.

Left and right injection can be used to gather two Simplicity types inside tuples.

Match (Case)

The real term name is Case but the case is a reserved keyword in Haskell. That’s why, the Haskell implementation of Simplicity’s case term is called`match` .

`match` takes two terms and, depending on the product given as parameter it will apply the first term or the second one. Match lets us implement conditions.

Pair

This one applies two terms to value and returns results inside a pair.

Take

Take apply a term given as a parameter to the first component of a pair.

Drop

Drop applies a term given as a parameter to the second component of a pair.

A term is a composition of combinators. So, a simplicity program is just a term applied to a stack.

## Simplicity primitives

The simplicity primitives provide features specific to a given blockchain. The core Simplicity contains the main feature whereas primitives provide features depending on the blockchain. For instance, the Elements’ primitive implement a term to issue an asset. The primitives of Bitcoin and Elements are provided inside the official source code repository.

## Simplicity program as Merkle tree

A Simplicity program can be represented as a set of combinators. In other words, a Simplicity program is a set of other Simplicity programs.

On the blockchain, it works a bit like a P2SH. But you do not pay to the hash of the Simplicity script. Instead of hash, a script is represented by the root of its Merkle tree. This tree is obtained by cutting the program into several branches. When the script is executed, the program is executed branch after branch.

Then, when a Simplicity script is executed, it must have the same Merkle root of the previously committed one.

The advantage is that you don’t lose the possibility to check the integrity of the script (we only need to calculate the Merkle root). And the tree architecture allows executing the program step by step: a branch is revealed only if the execution of the previous branch returned true.

# Simplicity first transaction

The purpose of this section is to reproduce Russel O’Connor’s transaction (the first official Simplicity transaction). Also described in the Next-Gen Smart Contracting Webinar.

The transaction consists to use the program CheckSigHashAll, the official example.

The CheckSigHashAll.hs file provides a checkSigHashAll Simplicity expression that verifies Schnorr signature
over the Bitcoin specific transaction data hash produced by sigHashAll for a provided public key.

In fact the official example program do two things:

1. First, it computes a transaction digest following an obsolete BIP-Schnorr signature.
2. Then, it checks if the signature given as parameter is valid for the computed transaction digest.

⚠ The official example can’t be used in production. Its purpose is pedagogical: Simplicity will evolve, and the official example will become obsolete. Moreover, key generation is not done in a cryptographically secure way.

## A word on Schnorr signature

The schnorr digital signature has the advantage of being mathematically simple. Given a message `m`, the Schnorr signature `s` is defined by the following expression:

Where:

• `k` is a random nonce.
• `e = H(m)` where H is a hashing function.
• `d` is the private key. Public key `p = d * G` where G is a generator.

The schnorr signature of a message `m` is the pair `(e, s)` . it allows doing amazing things. Let’s see an example: multi-signature.

Let’s imagine that Alice and Bob want to sign the message `m` . If they sign the hash of the message `e = H(m)` :

Where `Ka` and `Kb` are the random nonces choose by Alice and Bob. `Da` and `Db` are the private keys.

Then, if we add `Sa` and `Sb` :

So the sum of two signatures is the signature of the message using the sum of the private keys as private key.

Other examples of Schnorr signatures applications: https://www.youtube.com/watch?v=XKatSGCZ-gE

## Set up

The installation process is the same as the one we saw in the first part.

## Let’s go

Environnement configuration:

And generate some blocks:

Then use ghci with the Haskell package `Simplicity.`

Firstly, let’s import all the necessary Haskell modules:

This is a lot of modules, let’s change the prompt:

Then, create `asProgram` and `makeBinaryProgram` used to convert the Simplicity program to Binary.

The `putTermLengthCode` from the `Simplicity.Bitcoin.Jets` module just transform Simplicity expressions into a binary format.

Let’s create a public key for our Schnorr signature. We’ll use the `XOnlyPubKey` expression and a trivial public key value used for the official example.

Now we can create our program.

The `binaryDummySig` is the binary of our program. If you take a look at the `pkwCheckSigHashAll` source code, you’ll see that it returns a Term = a set of Simplicity combinators. You can also see that the signature (i.e the `sig `parameter) is the result of the `witness` function. It means that the `sig` will be excluded from the commitment Merkle Root computation. That’s why we use a `dummysig` for calculating our binaries.

Next, let’s define the `parseBinaryProgram` . It takes a binary program and returns its Simplicity expressions.

Next, compute the Merkle root of our program:

Let’s display the hash with a function `showHash` .

To resume, at this stage, the `cmr` expression will return the Merkle Root of the Simplicity program that verifies `dummysig` with `pubkey` .

You can use `showHash` to display the result on your screen:

What we computed here is the Merkle root of the `pkwCheckSigHashAll` program for the public key `pubkey` . The signature doesn’t count here.

For the next steps, we’ll use the module `Data.Text` , let’s import it:

And then use `bech32` to generate an address:

`hrp` is the prefix of bitcoin addresses. We also need the segwit version (non-valid one in this example) to generate the address.

The `encode,` `dataPartFromWords` and `dataPartFromBytes` expressions come from the `Codec.Binary.Bech32` module.

The expression above generates the bitcoin address according to the commitment Merkle Root of the program. Let’s display it:

You’ll probably get something like that:

Copy the address and put ghci in the background with `ctrl + Z `. Do not leave GHCI. We’ll use `bitcoin-cli` for a while.

Here we committed to a specific Simplicity program that verifies the Schnorr signature of a transaction digest for `pubkey` . This is the part very similar to P2SH.

From our newly transaction, let’s create a PSBT:

Then, update the outputs of the PSBT:

We can decode the transaction and visualize its details:

The transaction looks like the following:

We need the input’s txid of the PSBT transaction. You can use jq to select it:

You’ll probably get the following result:

Copy this value, we’ll need it soon. We also need the output script hex:

Who looks like this:

Then, go back on ghci with the `fg` command, do not forget to import the Simplicity package.

First, you can remove the `Data.Text` module.

Store the input txid inside an expression and create three other ones to store input configuration:

Same thing with the output script hex:

Some utility functions:

And the mkSigHashAll expression (a big one):

The `mkSigHashAll` expression uses Bitcoin primitives to get the transaction digest of `tx`. Here, the Haskell code do the same thing that the Simplicity program. We need to do that to get the signature (Note that it is not a secure way to get the digital signature. A real example should use libecp256k1 to compute the signature).

So we need to create the transaction to digest. Let’s create it using the values stored sooner.

First, create the inputs:

And the outputs:

Then we put them together in one transaction.

We can now apply `mkSigHashAll` to our transaction.

`sigHashAll` is the transaction digest of `tx` .

Then, Russel used some maths to get the digital signature using the transaction digest and the public key.

Note that the nonce’s value `k` here is not chosen randomly. That’s not a secure way to do things, don’t do that in production!

Then we can compute the binaries for the signature `sig` .

The `binary` is the simplicity program binaries that verify the schnorr signature `sig` for the public key `pubkey` .

Like the first time, we can display the Merkle root of the binary with `showHash` .

My Merkle root is the following:

The Commitment Merkle root hash must be the same that the first one. Only the `sig` parameter has changed, so the `cmr` is not affected.

Let’s write some utils function to “zoom” inside the binaries.

The `assembly` is our binaries write in a human-readable format.

The `showList` an expression can be used to see all the combinators of our program assembly. However, it can be very long, so the `showRange` expression can be used to select (and visualize) a range inside the program’s assembly.

Let’s take the first 10 expressions of the program:

This way you can explore the program in its Simplicity form. You can see here how Simplicity manipulates a stack of data.

Returning to our transaction, let’s write the binaries inside a file, we’ll use HAL to put the binaries inside a transaction:

Then leave ghci with `CTRL+D` and use HAL to create the raw transaction from the binary file.

First, we’ll use `hal psbt edit` to add the binary’s content to the witness data of our partially signed transaction.

Then transform our partially signed transaction to a transaction using `hal psbt finalize` .

You can display the transaction with `decoderawtransaction` :

Then let’s send the transaction on the network. This is when the Simplicity program runs.

Here, it is possible that you get an error code 26: `mandatory-script-verify-flag-failed` . This error can occur when the Merkle root of the Simplicity binaries used to build the transaction is not equal to the Merkle root of your program.

Next, validate the transaction by generating a block.

We’re now able to get our transaction.

If we select the hex value of the transaction and then decode it, we can see the outputs and the inputs.

We get the following result:

Congratulation! You’ve sent your first Simplicity transaction.

The `txinwitness` contains our simplicity program and the transaction is confirmed on the regtest network. The binaries of the `REDEEM_TX` must match the commitment Merkle root previously computed.

# Simplicity and Elements

It is theoretically possible to repeat the above process on an Elements network. The difference with bitcoin is the way the transactions are constructed. In particular, if you want to use Elements’ confidential transactions, the primitives of the Simplicity library for Elements allow you to handle them.

See you soon :)

## More From Medium

### Supply chain use case: Merkle Tree “off-chain”

Apr 10 · 12 min read

### I tested Elements (part 2) — Confidentials transactions

Mar 19 · 14 min read

### I tested Elements (part 1) — setup and first transactions

Mar 11 · 8 min read

#### 27

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just \$5/month. Upgrade