Getting Started
While developing Melodeon covenants, we want to be able to run and test our code without deploying it onto a blockchain. Generally, this means using a REPL or read-eval-print-loop.
There are currently two official REPL tools: an offline, command-line tool called melorun
, as well as an online playground.
Installing melorun
To install melorun
, first make sure your machine has Rust installed. Then, open a terminal and enter the following command:
cargo install melorun
Quick start
Once melorun
is installed, enter
melorun -i
in a terminal to fire up the REPL in interactive mode. Your terminal should display this prompt:
melorun>
Now, you can type any Melodeon expression following the prompt, hit enter, and the REPL will tell you what the expression evaluates to. It will also tell you the type of the expression after - :
, something we will discuss shortly.
Examples:
melorun> 5
- : Nat [more specifically: {5}]
5
melorun> 1 + 7
- : Nat [more specifically: {8}]
8
melorun> (1 + 2) * 3
- : Nat [more specifically: {9}]
9
Executing covenants
To execute a complete covenant file (these must end in .melo
) in the REPL, attach the file’s path to the end of the melorun
command:
melorun filename.melo
To interact with definitions from a .melo
file, add the -i
flag:
melorun -i filename.melo
Before going into two examples, recall that covenants are run in an execution environment consisting of, in part, the transaction that is trying to spend the coin locked by the covenant. What value the covenant produces then determines whether the transaction is valid.
Trivial example
Here’s an example of a very simple covenant. First, create a file named demo.melo
, with contents
def double(x: Nat) = 2*x
---
double(1)
This defines a function double
that takes in a non-negative number and doubles it. In the main body of the covenant (separated by ---
), we then call double with 1
.
Then, open a fresh terminal and make sure you’re in the same directory as demo.melo
. Next, type
melorun -i demo.melo
We see this output:
- : Nat
2 (truthy)
melorun>
This indicates that this covenant returned 2
, which is a truthy value. In short, this means that this covenant will allow whatever transaction tries to spend a coin locked by it.
💡 Aside: Since we used
melorun
’s interactive mode, you can play with thedouble
function in this REPL session:melorun> double(2) - : Nat 4 melorun> double(4+1) - : Nat 10
More interesting example
Edit demo.melo
to contain the following instead:
def double(x: Nat) = 2*x
---
double(vlen(env_spender_tx().outputs)) == 4
Now, this covenant instead requires that whatever transaction that spends the coin contain exactly 2 outputs. (see TODO documentation on env_spender_tx
and Transaction
)
Now if we run the covenant like this, we get a falsy value (meaning “transaction is rejected”):
$ melorun -i demo.melo
- : Nat [more specifically: {0..1}]
0 (falsy)
Hmm… what transaction is melorun
handing to the covenant and getting rejected? It turns out that melorun
by default populates the environment with a dummy transaction (more info in TODO):
melorun> env_spender_tx() - : Transaction [0, [[x"0000000000000000000000000000000000000000000000000000000000000000", 0]], [], 0, [x"420000"], "", []] melorun> env_spender_tx().outputs - : [CoinData;] []
We can instead specify a spend context, which contains further information about the transaction to generate for testing the covenant. Make a file context.yaml
containing:
spender_outputs:
0:
covhash: t48dn3f3k4xfevwwx97hzrgxmgcz3b6xw2sdnkfb6c6e3erqjq2sh0
denom: MEL
value: 10000
additional_data: ""
1:
covhash: t48dn3f3k4xfevwwx97hzrgxmgcz3b6xw2sdnkfb6c6e3erqjq2sh0
denom: MEL
value: 10000
additional_data: ""
This specifies two transaction outputs. Now, if we run
$ melorun -i demo.melo -s context.yaml
- : Nat [more specifically: {0..1}]
1 (truthy)
we get a “truthy” result. We can see that the dummy transaction now has outputs:
melorun> env_spender_tx().outputs
- : [CoinData;]
[[x"436a378e64ebddbe73a93c7f88769067c6b37782cb6b37accc3386ec5e571662", 10000, "m", ""],
[x"436a378e64ebddbe73a93c7f88769067c6b37782cb6b37accc3386ec5e571662", 10000, "m", ""]]