Uso de Marlowe desde la línea de comandos de ghci
Este tutorial muestra cómo utilizar Marlowe desde Haskell, y en particular muestra cómo ejercer un contrato utilizando la semántica dada anteriormente.
Marlowe en Haskell
Este tutorial funciona en la versión 3.0 de Marlowe que se encuentra en la rama master del repositorio:
Atravesar por los contratos
Como hemos visto antes, la semántica de una transacción individual está definida por la función
donde los tipos se definen así:
y States se definen así, con una función de ayuda para definir un estado inicialmente vacío:
Podemos utilizar las prestaciones de ghci para recorrer un contrato transacción por transacción, y, en este caso, lo haremos con el contrato de depósito en garantía incluido en `EscrowSimmpleV2.hs <https://github.com/input-output-hk/marlowe/blob/master/semantics-3.0/src/Language/Marlowe/Examples/EscrowSimpleV2.hs>`_.
Para ir paso a paso, puedes trabajar en ghci de esta manera, utilizando la facilidad de hacer enlaces locales:
Prelude> :set -XOverloadedStrings
Prelude> :l Language/Marlowe/Examples/EscrowSimpleV2.hs
...
*Lang...V2> let (TransactionOutput txWarn1 txPay1 state1 con1) = computeTransaction (TransactionInput (0, 0) [IDeposit "alice" "alice" ada 450]) (emptyState 0) contract
Al hacer esto, hemos comparado el patrón de salida de una aplicación de computeTransaction, que toma tres entradas: la segunda es un estado inicial (en el slot número 0) y la tercera es el contrato de custodia inicial. La primera es un TransactionInput que contiene un SlotInterval – aquí SlotInterval 0 0 – y un depósito de 450 Lovelace de "alice" a su cuenta "alice" , es decir, IDeposit "alice" "alice" ada 450.
Nota
Si quieres probar esto por ti mismo en ghci, puedes copiar y pegar de los ejemplos de código en el documento original (ver enlace al final de la página): están en ventanas de desplazamiento horizontal.
La salida está relacionada con TransactionOutput txWarn1 txPay1 state1 con1 para poder examinar los distintos componentes por separado:
*Lang...V2> txWarn1
[]
*Lang...V2> txPay1
[]
*Lang...V2> state1
State {accounts = fromList [("alice", ada), 450)], choices = fromList [], boundValues = fromList [], minSlot = 0}
*Lang...V2> con1
When [Case (Choice (ChoiceId "choice" "alice") [Bound 0 1])
...
Esto muestra que la transacción no genera advertencias ni pagos, pero actualiza el estado para mostrar el saldo en la cuenta "alice", y actualiza el contrato, listo para recibir una elección de Alice o Bob.
En el siguiente estado el contrato está a la espera de la entrada, y si tanto Alice como Bob están de acuerdo en hacer un pago a Bob eligiendo 0, entonces se genera un pago a Bob. Esto se verifica a través de esta interacción en GHCI:
*Lang...V2> let (TransactionOutput txWarn2 txPay2 state2 con2) = computeTransaction (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "alice") 0, IChoice (ChoiceId "choice" "bob") 0]) state1 con1
*Lang...V2> txPay2
[Payment "bob" ada 450]
*Lang...V2> con2
Close
*Lang...V2> state2
State {accounts = fromList [], choices = fromList [(ChoiceId "choice" "alice",0),(ChoiceId "choice" "bob",0)], boundValues = fromList [], minSlot = 0}
Una forma alternativa de hacerlo es añadir estas definiciones a un archivo de trabajo, por ejemplo Build.hs, donde se conservarán estas definiciones. De hecho, sería muy sensato incluir algunas de las definiciones utilizadas anteriormente en dicho archivo.
Rutas alternativas a través del contrato
Los enlaces locales se pierden cada vez que un comando :load o :l se ejecuta, lo que nos permite reutilizar algunos comandos anteriores. Una ejecución alternativa del contrato viene dada por
- Primer paso: Alicia deposita el dinero como en el ejemplo anterior.
- Segundo paso: Alice y Bob seleccionan diferentes opciones. Esto puede hacerse así:
*Lang...V2> let (TransactionOutput txWarn2 txPay2 state2 con2) = computeTransaction (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "alice") 0, IChoice (ChoiceId "choice" "bob") 1]) state1 con1
*Lang...V2> con2
When [Case (Choice (ChoiceId "choice" "carol") [Bound 1 1]) Close, Case (Choice (ChoiceId "choice" "carol") [Bound 0 0]) (Pay "alice" (Party "bob") ada (Constant 450) Close)] 100 Close
*Lang...V2> state2
State {accounts = fromList [("alice", ada), 450)], choices = fromList [(ChoiceId "choice" "alice",0),(ChoiceId "choice" "bob",1)], boundValues = fromList [] , minSlot = 0}
Esto muestra que ahora estamos en un contrato en el que la elección depende de Carol, y que todavía existen los 450 Lovelace en la cuenta "alice" .
- Tercer paso: Carol hace una elección. Si elige 0, se paga a Bob. Si elige 1, se devuelve el dinero a Alice. Hagamos esto ahora:
*Lang...V2> let (TransactionOutput txWarn3 txPay3 state3 con3) = computeTransaction (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "carol") 1]) state2 con2
*Lang...V2> txPay3
[Payment "alice" ada 450]
*Lang...V2> con3
Close
*Lang...V2> state3
State {accounts = fromList [], choices = fromList [(ChoiceId "choice" "alice",0), (ChoiceId "choice" "bob",1),(ChoiceId "choice" "carol",1)], boundValues = fromList [], minSlot = 0}
Así que ahora el contrato está listo para Close, y así devolver el dinero restante, pero está claro por state3 que no hay cuentas con saldos distintos de cero, por lo que el contrato se termina.
¿Por qué es útil ir paso a paso? Es el equivalente a un debugging, y podemos ver el estado interno del contrato en cada etapa, la continuación del contrato, es decir, lo que queda por ejecutar, y las acciones producidas en cada paso.
Ejercicio
Explora otras formas de abordar el contrato: ¿qué ocurre si Bob y Alice deciden devolver el dinero a Alice? - ¿Qué ocurre si Bob y Alice no están de acuerdo, pero Carol se pone del lado de Bob?
© Copyright 2020, IOHK Revision 1b7b1fc1.
Encuentra una copia oficial de este documento aquí:
https://alpha.marlowe.iohkdev.io/doc/marlowe/tutorials/using-marlowe.html
https://docs.cardano.org/projects/plutus/en/latest/marlowe/tutorials/using-marlowe.html
Más traducciones de Cardano en: Cardano For The World