# 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.

If you want to learn more about functional programming, begin with the lambda calculus. The Computerphile Youtube channel has an excellent video about it: https://youtu.be/eis11j_iGMs

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:

- Simplicity code source,
*describe how the language works and investigates the Haskell implementation.* - 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:

`git clone https://github.com/ElementsProject/simplicity.git`

cd simplicity/Haskell

## 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

Haskell Types module: https://github.com/ElementsProject/simplicity/blob/master/Haskell/Core/Simplicity/Ty.hs

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:

`intance TyC ()`

instance (TyC a, TyC b) => TyC (Either a b)

instance (TyC a, TyC b) => TyC (a, b)

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:

-- The following expressions' types don't have an instance of the TyC classlist :: [Int]

list = [1, 2, 6, 200]tuple3 :: (Int, String, String)

tuple3 = (3, "foo", "foo")eitherIntOrString :: Either Int String

eitherIntOrString = Left 3

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

-- The following expressions' types have a TyC instanceemptyTuple :: ()

emptyTuple = ()tupleOfEmptyTuples :: ((), ())

tupleOfEmptyTuples = ((), ())either0 :: Either () ((), ())

either0 = Right tupleOfEmptyTupleseither1 :: Either (((), ()), Either ((), ((), ())) ((), ())) ()

either1 = Right ()

⚠ 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.

`data TyReflect a where `

OneR :: TyReflect ()

SumR :: (TyC a, TyC b) => TyReflect a -> TyReflect b -> TyReflect (Either a b)

ProdR :: (TyC a, TyC b) => TyReflect a -> TyReflect b -> TyReflect (a, b)

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:

-- The following expressions' types have a TyC instance (Rewrite with TyReflect.emptyTuple :: TyReflect ()

emptyTuple = OneRtupleOfEmptyTuples :: TyReflect ((), ())

tupleOfEmptyTuples = ProdR OneR OneReither0 :: TyReflect (Either () ((), ()))

either0 = SumR OneR tupleOfEmptyTupleseither1 :: TyReflect (Either (((), ()), Either ((), ((), ())) ((), ())) ())

either1 = SumR (ProdR (ProdR OneR OneR) (SumR (ProdR OneR (ProdR OneR OneR)) (ProdR OneR OneR))) OneR

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:

`type Bit = Either () ()`

And canonically map it to the Haskell type `Bool`

:

fromBit :: Bit -> Bool

fromBit (Left ()) = False

fromBit (Right ()) = TruetoBit :: Bool -> Bit

toBit False = Left ()

toBit True = Right ()

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**

`iden :: TyC a => term a a`

iden = id

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

**Composition**

`comp :: (TyC a, TyC b, TyC c) => term a b -> term b c -> term a c`

comp s t = t . s

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

**Constant Unit**

`unit :: TyC a => term a ()`

unit = const ()

This one is simple, it always returns `()`

whatever the type or the value of the argument.

**Left Injection**

`injl :: (TyC a, TyC b, TyC c) => term a b -> term a (Either b c)`

injl t a = Left (t a)

We take a term `t`

applied to `a`

and compose it with the Left constructor of the Either data type.

**Right Injection**

`injr :: (TyC a, TyC b, TyC c) => term a c -> term a (Either b c)`

injr t a = Right (t a)

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 :: (TyC a, TyC b, TyC c, TyC d) => term (a, c) d -> term (b, c) d -> term (Either a b, c) d`

match s _ (Left a, c) = s (a, c)

match _ t (Right b, c) = t (b, c)

`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**

`pair::(TyC a, TyC b, TyC c) => term a b -> term a c -> term a (b, c)`

pair s t a = (s a, t a)

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

**Take**

`take :: (TyC a, TyC b, TyC c) => term a c -> term (a, b) c`

take t (a, _) = t a

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

**Drop**

`drop :: (TyC a, TyC b, TyC c) => term b c -> term (a, b) c`

drop t (_, b) = t b

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:

- First, it computes a transaction digest following an obsolete BIP-Schnorr signature.
- 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:

`s = k - ed`

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)`

:

`Sa = Ka - (e * Da)`

Sb = Kb - (e * Db)

*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`

:

`Sa + Sb = Ka + Kb - e * Da - e * Db`

= (Ka + Kb) - e * (Da + Db)

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

`s(k, d) = k - e * d`

# property allowing multisignatures

s(k1, d1) + s(k2, d2) = s(k1 + k2, d1 + d2)

# public key to verify = (d1 + d2)G

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:

`$ cd $HOME`

$ ./elementsEnv.sh

Create a new address:

`$ ADDRESS=$(btc getnewaddress)`

And generate some blocks:

`$ btc generatetoaddress 102 $ADDRESS`

Then use ghci with the Haskell package `Simplicity.`

`ghci -package Simplicity`

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

`:m + Simplicity.Bitcoin.Jets Simplicity.LibSecp256k1.Schnorr Simplicity.Serialization Simplicity.MerkleRoot Simplicity.Digest Codec.Binary.Bech32 Simplicity.Bitcoin.Programs.CheckSigHashAll.Lib Simplicity.Digest Simplicity.Bitcoin.DataTypes Simplicity.Bitcoin.Dag Simplicity.Bitcoin.Inference`

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

`:set prompt " > "`

Then, create `asProgram`

and `makeBinaryProgram`

used to convert the Simplicity program to Binary.

`let asProgram = id :: a () () -> a () ()`

let makeBinaryProgram = putTermLengthCode . asProgram

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.

`let order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141`

let priv = order `div` 2

let pubkey = XOnlyPubKey 0x00000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63

Now we can create our program.

-- Signature to verify with pubKey

let dummysig = Sig 0 0-- Create the binary of the simplicity program pkwCheckSigHashAll applied to pubkey and dummysig

let binaryDummySig = makeBinaryProgram $ pkwCheckSigHashAll pubkey dummysig

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.

`let parseBinaryProgram bp = let Right x = evalStreamWithError getTermLengthCode bp in asProgram x`

Next, compute the Merkle root of our program:

`let cmr = commitmentRoot . parseBinaryProgram $ binaryDummySig`

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

.

`let showHash = flip Numeric.showHex "" . integerHash256`

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:

`showHash cmr`

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:

`:m + Data.Text`

And then use `bech32`

to generate an address:

`let Right hrp = humanReadablePartFromText . Data.String.fromString $ "bcrt"`

let simplictySegwitVersion = (-1) `mod` 32

`hrp`

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

`let Right address = encode hrp (dataPartFromWords [toEnum simplictySegwitVersion] <> (dataPartFromBytes . Data.Serialize.encode $ cmr))`

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:

`address`

You’ll probably get something like that:

`"bcrt1lfvl3m3f0zea3s4smzapjm07258mxt8g7guut4rmmuqzzy60l7c4qfucdch"`

Copy the address and put ghci in the *background *with `ctrl + Z `

. **Do not leave GHCI. **We’ll use `bitcoin-cli`

for a while.

`TXID=$(btc sendtoaddress "bcrt1lfvl3m3f0zea3s4smzapjm07258mxt8g7guut4rmmuqzzy60l7c4qfucdch" 1.0001 "" "" false)`

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:

`PSBT=$(btc createpsbt "[{\"txid\": \"$TXID\", \"vout\": 1, \"sequence\": 4293967294}]" "[{\"$ADDRESS\": \"1.0\"}]")`

Then, update the outputs of the PSBT:

`PSBT=$(btc utxoupdatepsbt $PSBT)`

We can decode the transaction and visualize its details:

`btc decodepsbt $PSBT | jq `

The transaction looks like the following:

`{`

"tx": {

"txid": "b29d917349c5c9e5dbcbd11ca3c85d011c4d8462ea46afdbd18f336b05adfbff",

"hash": "b29d917349c5c9e5dbcbd11ca3c85d011c4d8462ea46afdbd18f336b05adfbff",

"version": 2,

"size": 83,

"vsize": 83,

"weight": 332,

"locktime": 0,

"vin": [

{

"txid": "6da5a566c22ec74f4a197e4a7cde48e9f4822796bb80b3030fd18626937b6f08",

"vout": 1,

"scriptSig": {

"asm": "",

"hex": ""

},

"sequence": 4294967294

}

],

"vout": [

{

"value": 1,

"n": 0,

"scriptPubKey": {

"asm": "OP_HASH160 21bb834e651666a16cb414879fb08f8d9e21a12f OP_EQUAL",

"hex": "a91421bb834e651666a16cb414879fb08f8d9e21a12f87",

"reqSigs": 1,

"type": "scripthash",

"addresses": [

"2MvKavEsw2tvJWMt8cjYjipo9DkVwarby16"

]

}

}

]

},

"unknown": {},

"inputs": [

{

"witness_utxo": {

"amount": 1.0001,

"scriptPubKey": {

"asm": "-1 4b3f1dc52f167b18561b17432dbfcaa1f6659d1e4738ba8f7be0042269fff62a",

"hex": "4f204b3f1dc52f167b18561b17432dbfcaa1f6659d1e4738ba8f7be0042269fff62a",

"type": "witness_unknown",

"address": "bcrt1lfvl3m3f0zea3s4smzapjm07258mxt8g7guut4rmmuqzzy60l7c4qfucdch"

}

}

}

],

"outputs": [

{}

],

"fee": 0.0001

}

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

`btc decodepsbt $PSBT | jq ".tx.vin[0].txid" | tr -d '"'`

You’ll probably get the following result:

`386105c4a514fc6808f4d607734df2249300730c520e60b4e42636b6a33d03ca`

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

`btc decodepsbt $PSBT | jq ".tx.vout[0].scriptPubKey.hex" | tr -d '"'`

Who looks like this:

`a91421bb834e651666a16cb414879fb08f8d9e21a12f87`

Then, go back on ghci with the `fg`

command, do not forget to import the Simplicity package.

`fg`

First, you can remove the `Data.Text`

module.

`:m - Data.Text`

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

`# Replace inputTxid by yours.`

let inputTxid = "386105c4a514fc6808f4d607734df2249300730c520e60b4e42636b6a33d03ca"

let inputVout = 1

let inputValue = 100010000

let inputSequence = 4293967294

Same thing with the **output script hex:**

`# replace my outputScript hex value `

let outputScript = "a91421bb834e651666a16cb414879fb08f8d9e21a12f87"

let outputValue = 100000000

Some utility functions:

`let groupBy2 = Data.List.unfoldr (\l -> if null l then Nothing else Just (splitAt 2 l))`

let readHex = fst . head . Numeric.readHex

let readTxid = readHex . concat . reverse . groupBy2

let parseScript = Data.ByteString.Lazy.pack . fmap readHex . groupBy2

let sigHashAllPrefix = Data.Digest.Pure.SHA.padSHA1 . Data.ByteString.Lazy.fromStrict $ Data.ByteString.Char8.pack "Simplicity\USSignature\GS" <> Data.Serialize.encode Simplicity.Bitcoin.Programs.CheckSigHashAll.Lib.sigAllCMR

And the mkSigHashAll expression (a big one):

`let mkSigHashAll tx ix = bslHash . Data.Serialize.runPutLazy $ do { Data.Serialize.putLazyByteString sigHashAllPrefix; Data.Serialize.put (sigTxInputsHash tx); Data.Serialize.put (sigTxOutputsHash tx); Data.Serialize.putWord64be (sigTxiValue (sigTxIn tx Data.Array.! ix)); Data.Serialize.putWord32be ix; Data.Serialize.putWord32be (sigTxLock tx); Data.Serialize.putWord32be (sigTxVersion tx) }`

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:

`let inputs = Data.Array.listArray (0, 0) [SigTxInput { sigTxiPreviousOutpoint = Outpoint (Lens.Family2.review (Lens.Family2.over be256) (readTxid inputTxid)) inputVout, sigTxiValue = inputValue, sigTxiSequence = inputSequence }]`

And the outputs:

`let outputs = Data.Array.listArray (0,0) [TxOutput {txoValue = outputValue, txoScript = parseScript outputScript }]`

Then we put them together in one transaction.

`let tx = SigTx { sigTxVersion = 2, sigTxIn = inputs, sigTxOut = outputs, sigTxLock = 0 }`

We can now apply `mkSigHashAll`

to our transaction.

`let sigHashAll = mkSigHashAll tx 0`

`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.

let r = 0x00000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63 :: Simplicity.Word.Word256let schnorrTag = Data.Serialize.encode . bsHash $ Data.ByteString.Char8.pack "BIPSchnorr"let e = integerHash256 . bsHash $ schnorrTag <> schnorrTag <> Data.Serialize.encode r <> Data.Serialize.encode pubkey <> Data.Serialize.encode sigHashAlllet k = order `div` 2let sig = Sig r (fromInteger ((k + priv * e) `mod` order))

*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`

.

`let binary = makeBinaryProgram $ pkwCheckSigHashAll pubkey 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`

.

`showHash . commitmentRoot . parseBinaryProgram $ binary`

My Merkle root is the following:

`"4b3f1dc52f167b18561b17432dbfcaa1f6659d1e4738ba8f7be0042269fff62a"`

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.

let disassemble x = jetDag (x :: JetDag JetType () ())let stripTypeAnnotations = Lens.Family2.set (traverse . tyAnnotation) ()let showAssembly = zipWith (\i x -> show i ++ ": " ++ show (fmap (i -) x)) [0..]let assembly = showAssembly . stripTypeAnnotations . disassemble . parseBinaryProgram $ binarylet showList = mapM_ putStrLnlet showRange x i a = showList $ (take i . drop x) a

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:

`showRange 0 10 assembly `

-- Result:

0: Unit ()

1: Injl () () () 0

2: Pair () () () 1 1

3: Pair () () () 2 2

4: Pair () () () 3 3

5: Pair () () () 4 4

6: Pair () () () 5 5

7: Pair () () () 6 6

8: Injr () () () 0

9: Pair () () () 8 8

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:

let writeBinaryToFile fn = Data.ByteString.writeFile fn . Data.Serialize.runPut . putBitStreamwriteBinaryToFile "/dev/shm/checksighashall.simplicity" binary

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.

`PSBT_EDITED=$(hal psbt edit --input-idx 0 --final-script-witness $(hexdump -v -e'1/1 "%02x"' /dev/shm/checksighashall.simplicity) $PSBT)`

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

.

`REDEEM_TX=$(hal psbt finalize $PSBT_EDITED)`

You can display the transaction with `decoderawtransaction`

:

`btc decoderawtransaction $REDEEM_TX`

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

`REDEEM_TXID=$(btc sendrawtransaction $REDEEM_TX)`

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.

`btc generatetoaddress 1 $ADDRESS`

We’re now able to get our transaction.

`btc gettransaction $REDEEM_TXID`

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

`HEX=$(btc gettransaction $REDEEM_TXID | jq '.hex' | tr -d '"')`

btc decoderawtransaction $HEX

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 :)