Monolithic Workflow
This tutorial will demonstrate a monolithic workflow in Marlowe. Monolithic refers to all the inputs passed to marlowe-cli
transformed to a JSON file then parsed as a transaction. cardano-cli
will be used to query the state of the transaction.
There will cover these main topics:
- Getting and passing all the inputs needed for
marlowe-cli contract marlowe
- Parsing the Marlowe JSON file to create a transaction with
marlowe-cli transaction
- Obtaining and retuning testnet ADA from the faucet
Prerequisites
An installation of the following tools is required:
marlowe-cli
cardano-cli
jq
Start a Cardano Node with Docker
Open a terminal and start a Cardano node in a container with the node-ipc
volume:
docker run -e NETWORK=preprod -v node-ipc:/ipc inputoutput/cardano-node
Development Environment
If the tools above are not yet installed, open a new terminal. Start a nix shell from the Marlowe starter kit:
nix develop github:input-output-hk/marlowe-starter-kit
Set the node socket and testnet
Using the node-ipc
volume name, inspect the node socket and set the path to CARDANO_NODE_SOCKET_PATH
.
Then allow read and write from that path.
export CARDANO_NODE_SOCKET_PATH=$(docker volume inspect node-ipc | jq -r '.[0].Mountpoint')/node.socket
f=$(dirname $CARDANO_NODE_SOCKET_PATH)
while [[ $f != / ]]; do sudo chmod a+rx "$f"; f=$(dirname "$f"); done
sudo chmod a+rwx $CARDANO_NODE_SOCKET_PATH
Assuming the preprod testnet is used, set the environment variable:
export CARDANO_TESTNET_MAGIC=1
This enviroment variable will be used across marlowe-cli
and cardano-cli
for building transactions.
Wallet
PAYMENT_SKEY=payment.skey
PAYMENT_VKEY=payment.vkey
if [[ ! -e "$PAYMENT_SKEY" ]]
then
cardano-cli address key-gen \
--signing-key-file "$PAYMENT_SKEY" \
--verification-key-file "$PAYMENT_VKEY"
fi
PAYMENT_ADDR=$(cardano-cli address build --testnet-magic "$CARDANO_TESTNET_MAGIC" --payment-verification-key-file "$PAYMENT_VKEY" )
echo "$PAYMENT_ADDR" payment.address
echo "PAYMENT_ADDR = $PAYMENT_ADDR"
Go to the testnet faucet, select the preprod
network and paste the payment address. It may take a few minutes for the transaction to complete. Check the status using the cardano-cli
.
cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR
It is also possible the node is not fully synced. Check the status of the node with cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC
.
Create a contract
MARLOWE_FILE=monolithic.marlowe
PLUTUS_FILE=monolithic.plutus
DATUM_FILE=monolithic.datum
REDEEMER_FILE=monolithic.redeemer
CONTRACT_FILE=monolithic.contract
STATE_FILE=monolithic.state
echo \"close\" >> monolithic.contract
DATUM_LOVELACE=3000000
cat << EOI > $STATE_FILE
{
"choices": [],
"accounts": [
[
[
{
"address": "$PAYMENT_ADDR"
},
{
"currency_symbol": "",
"token_name": ""
}
],
$DATUM_LOVELACE
]
],
"minTime": 10,
"boundValues": []
}
EOI
marlowe-cli contract marlowe --testnet-magic $CARDANO_TESTNET_MAGIC \
--contract-file $CONTRACT_FILE \
--state-file $STATE_FILE \
--out-file $MARLOWE_FILE \
--print-stats
Bare-validator cost: ExBudget {exBudgetCPU = ExCPU 18745100, exBudgetMemory = ExMemory 81600}
Validator size: 12296
Datum size: 85
Redeemer size: 3
Total size: 12384
Preparing a transaction
Check the Marlowe JSON file has the expected output. These will be passed into marlowe-cli
once again.
jq 'to_entries[] | .key' $MARLOWE_FILE
If set up correctly, there should be three expected fields.
"datum"
"redeemer"
"validator"
Validator Plutus File
Create a monolithic.plutus
file.
jq '.validator.script' $MARLOWE_FILE > $PLUTUS_FILE
Validator Address
Create a script address.
ADDRESS_S=$(jq -r '.validator.address' $MARLOWE_FILE)
echo $ADDRESS_S
Datum Hash
DATUM_HASH=$(jq -r '.datum.hash' $MARLOWE_FILE)
echo $DATUM_HASH
Datum
Create a monolithic.datum
file.
jq '.datum.json' $MARLOWE_FILE > $DATUM_FILE
Redeemer
jq '.redeemer.json' $MARLOWE_FILE > $REDEEMER_FILE
Funds to send
Check the address has been funded with ADA from the previous step.
cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR
If succcessful, there will be a TxHash for the address.
TxHash TxIx Amount
--------------------------------------------------------------------------------------
4ea15f2c853535cc7fa16f9b9b4184d02bac75812a5448ec62be848ef3519572 0 10000000000 lovelace + TxOutDatumNone
Set TX_0
to the TxHash
of this transaction appended by #
along with the TxIx
.
TX_0="4ea15f2c853535cc7fa16f9b9b4184d02bac75812a5448ec62be848ef3519572#0"
marlowe-cli transaction create --testnet-magic $CARDANO_TESTNET_MAGIC \
--socket-path "$CARDANO_NODE_SOCKET_PATH" \
--script-address "$ADDRESS_S" \
--tx-out-datum-file $DATUM_FILE \
--tx-out-marlowe $DATUM_LOVELACE \
--tx-in "$TX_0" \
--change-address "$PAYMENT_ADDR" \
--out-file tx.raw \
--required-signer $PAYMENT_SKEY \
--print-stats \
--submit=600
Once the transaction is successfully created, an associated
TxId` will be available.
Fee: Lovelace 176721
Size: 291 / 16384 = 1%
Execution units:
Memory: 0 / 14000000 = 0%
Steps: 0 / 10000000000 = 0%
TxId "57d01eaf51bc49ce58961e4353016ac2c9467075b783c12cef6f973ef82835cf"
Set TX_1
to be the TxId
of the previous output.
TX_1=57d01eaf51bc49ce58961e4353016ac2c9467075b783c12cef6f973ef82835cf
Verify the contract address now contains the 3 ADA.
cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address "$ADDRESS_S" | sed -n -e "1p; 2p; /$TX_1/p"
The output should be similar to below. Observe that TxOutDatumHash
is identical to $DATUM_HASH
from the contract inputs earlier.
TxHash TxIx Amount
--------------------------------------------------------------------------------------
57d01eaf51bc49ce58961e4353016ac2c9467075b783c12cef6f973ef82835cf 1 3000000 lovelace + TxOutDatumHash ScriptDataInBabbageEra "960c278f6524c93de651f314304ccc6c8d30bf34c986834cc27e4bdff59527d0"
Close the contract
Before closing the contract, a redeem slot range must be provided.
$ cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC
{
"block": 1042141,
"epoch": 75,
"era": "Babbage",
"hash": "19a58b0e3832e2857c1eeab01d7ab87f8cf98903ef29249148574df2817062d1",
"slot": 30864376,
"syncProgress": "100.00"
}
Choose a valid slot range so that the transaction can close in the next step. Depending on the time of execution and testnet environment, the values may appear different.
REDEEM_MIN_SLOT=30860000
REDEEM_MAX_SLOT=30960000
$ marlowe-cli transaction close --testnet-magic $CARDANO_TESTNET_MAGIC \
--socket-path "$CARDANO_NODE_SOCKET_PATH" \
--tx-in-script-file $PLUTUS_FILE \
--tx-in-redeemer-file $REDEEMER_FILE \
--tx-in-datum-file $DATUM_FILE \
--tx-in-marlowe "$TX_1#1" \
--tx-in "$TX_1#0" \
--tx-in-collateral "$TX_1#0" \
--tx-out "$PAYMENT_ADDR"+$DATUM_LOVELACE \
--change-address "$PAYMENT_ADDR" \
--invalid-before $REDEEM_MIN_SLOT \
--invalid-hereafter $REDEEM_MAX_SLOT \
--out-file tx.raw \
--required-signer $PAYMENT_SKEY \
--print-stats \
--submit=600
Fee: Lovelace 947046
Size: 12738 / 16384 = 77%
Execution units:
Memory: 2818820 / 14000000 = 20%
Steps: 738349482 / 10000000000 = 7%
TxId "931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde"
Checking the address again, there will be a new transaction with the 3 ADA.
cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR
TxHash TxIx Amount
--------------------------------------------------------------------------------------
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 0 9995876233 lovelace + TxOutDatumNone
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 1 3000000 lovelace + TxOutDatumNone
Clean-up
It is considered a good practice to return test tokens to maintain the health of the testnet.
Querying the state of the address that received the testnet ADA, obtain the TxHash
. In this example, it is a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a
.
cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR
TxHash TxIx Amount
--------------------------------------------------------------------------------------
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 1 3000000 lovelace + TxOutDatumNone
a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a 0 9995705936 lovelace + TxOutDatumNone
Build a transaction with the testnet address to return the ADA in --tx-out
. The output, represented by +0
will be updated after calculating the --fee
. To obtain a slot for --invalid-hereafter
, run cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC
for a valid slot.
cardano-cli transaction build-raw --tx-in a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a#0 \
--tx-out addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3+0 \
--invalid-hereafter 30960000 \
--fee 0 \
--out-file tx.draft
Next, obtain the protocol parameters.
cardano-cli query protocol-parameters --testnet-magic $CARDANO_TESTNET_MAGIC --out-file protocol.json
Then calculate the fee. Subtract the free from the amount above 9995705936 - 170297
.
cardano-cli transaction calculate-min-fee --tx-body-file tx.draft --tx-in-count 1 --tx-out-count 1 --witness-count 1 --testnet-magic $CARDANO_TESTNET_MAGIC --protocol-params-file protocol.json
170297 Lovelace
Replace the +0
with the calulated amount and set the fee accordingly. It is important that the fee and output is equal to the inputs otherwise the transaction will fail.
cardano-cli transaction build-raw --tx-in a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a#0 \
--tx-out addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3+9995534759 \
--invalid-hereafter 30960000 \
--fee 171177 \
--out-file tx.draft
Finally sign and submit the transaction.
cardano-cli transaction sign --tx-body-file tx.draft --signing-key-file payment.skey --testnet-magic $CARDANO_TESTNET_MAGIC --out-file tx.signed
cardano-cli transaction submit --tx-file tx.signed --testnet-magic $CARDANO_TESTNET_MAGIC
Transaction successfully submitted.