FlowCards: Un Marco Declarativo para el Desarrollo de dApps en Ergo
29 de abril de 2020

Agradecimientos a Robert Kornacki por pulir el borrador.
Introducción
ErgoScript es el lenguaje de contratos inteligentes utilizado
por la blockchain de Ergo. Aunque tiene una sintaxis concisa adoptada de Scala/Kotlin, aún puede
parecer confuso al principio porque conceptualmente ErgoScript es bastante diferente en comparación
con los lenguajes convencionales que todos conocemos y amamos. Esto se debe a que Ergo es una blockchain
basada en UTXO, mientras que los contratos inteligentes se asocian tradicionalmente con sistemas basados en
cuentas como Ethereum. Sin embargo, el modelo de transacciones de Ergo tiene muchas ventajas sobre el modelo
basado en cuentas y con el enfoque correcto puede ser incluso significativamente más fácil desarrollar contratos
Ergo que escribir y depurar código Solidity.
A continuación, cubriremos los aspectos clave del modelo de contrato de Ergo que lo hacen diferente:
Paradigma
El modelo de cuentas de Ethereum es imperativo. Esto significa que la tarea típica de enviar monedas de
Alice a Bob requiere cambiar los saldos en el almacenamiento como una serie de operaciones. El modelo de programación
basado en UTXO de Ergo, por otro lado, es declarativo. Los contratos ErgoScript especifican condiciones para
que una transacción sea aceptada por la blockchain (no cambios que se realicen en el estado de almacenamiento
como resultado de la ejecución del contrato).
Escalabilidad
En el modelo de cuentas de Ethereum, tanto los cambios de almacenamiento como las verificaciones de validez se realizan
en la cadena durante la ejecución del código. En contraste, las transacciones de Ergo se crean
fuera de la cadena y solo se realizan verificaciones de validación en la cadena, reduciendo así la cantidad de
operaciones realizadas por cada nodo en la red. Además, debido a la inmutabilidad del
grafo de transacciones, son posibles varias estrategias de optimización para mejorar el rendimiento
de transacciones por segundo en la red. También son posibles nodos de verificación ligeros, facilitando aún más
la escalabilidad y accesibilidad de la red.
Estado compartido
El modelo basado en cuentas depende de un estado mutable compartido que se sabe que conduce
a semánticas complejas (y sutiles errores de millones de dólares) en el contexto de computación concurrente/
distribuida. El modelo de Ergo se basa en un grafo inmutable de transacciones. Este
enfoque, heredado de Bitcoin, se adapta bien a la naturaleza concurrente y distribuida de
las blockchains y facilita clientes ligeros sin confianza.
Poder expresivo
Ethereum abogó por la ejecución de un lenguaje Turing-completo en la blockchain. Teóricamente prometió un potencial ilimitado, sin embargo, en la práctica surgieron severas
limitaciones debido a la excesiva sobrecarga de la blockchain, sutiles errores de millones de dólares, costos de gas que
limitan la complejidad del contrato y otros problemas similares. Ergo, por otro lado, extiende UTXO para habilitar
la Turing-completitud mientras limita la complejidad del lenguaje ErgoScript en sí. El mismo
poder expresivo se logra de una manera diferente y más semánticamente sólida.
Con todos los puntos anteriores, debería quedar claro que hay muchos beneficios en el modelo que utiliza Ergo.
En el resto de este artículo, te presentaré el concepto de FlowCards: un componente para desarrolladores de dApp
que permite diseñar contratos Ergo complejos de manera declarativa y visual.
De Imperativo a Declarativo
En el modelo de programación imperativa de Ethereum, una transacción es una secuencia de operaciones
ejecutadas por la VM de Ethereum. La siguiente función Solidity
implementa una transferencia de tokens de sender a receiver. La transacción comienza cuando
sender llama a esta función en una instancia de un contrato y termina cuando la función
retorna.
// Envía una cantidad de monedas existentes de cualquier llamador a una dirección
function send(address receiver, uint amount) public {
require(amount <= balances[msg.sender], "Saldo insuficiente.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
La función primero verifica las precondiciones, luego actualiza el almacenamiento (es decir, saldos) y
finalmente publica la postcondición como el evento Sent. El gas que consume la
transacción se envía al minero como recompensa por ejecutar esta transacción.
A diferencia de Ethereum, una transacción en Ergo es una estructura de datos que contiene una lista de monedas de entrada
que gasta y una lista de monedas de salida que crea, preservando los saldos totales de
ERGs y tokens (en lo que Ergo es similar a Bitcoin).
Volviendo al ejemplo anterior, dado que Ergo admite nativamente tokens, por lo tanto, para este
ejemplo específico de envío de tokens no necesitamos escribir ningún código en ErgoScript. En su lugar,
necesitamos crear la transacción 'send' mostrada en la figura siguiente, que describe
la misma transferencia de tokens pero de manera declarativa.

La imagen describe visualmente los siguientes pasos que el usuario de la red necesita realizar:
- Seleccionar cajas no gastadas del remitente, que contengan en total
tB >= amountde tokens yB >= txFee + minErgERGs. - Crear una caja de salida
targetque esté protegida por la clave pública delreceiverconminErg
ERGs yamountdeTtokens. - Crear una salida de tarifa protegida por el contrato
minerFeecontxFeeERGs. - Crear una salida de cambio protegida por la clave pública del
sender, que contenga
B - minErg - txFeeERGs ytB - amountdeTtokens. - Crear una nueva transacción, firmarla usando la clave secreta del remitente y enviarla a la red de Ergo.
Lo importante a entender aquí es que todos estos pasos se realizan fuera de la cadena (por
ejemplo, usando la API de Transacciones de Appkit) por la
aplicación del usuario. Los nodos de la red Ergo no necesitan repetir este proceso de creación de transacciones,
solo necesitan validar la transacción ya formada. Los contratos ErgoScript se almacenan en
las entradas de la transacción y verifican las condiciones de gasto. El nodo ejecuta los
contratos en la cadena cuando la transacción es validada. La transacción es válida si se satisfacen todas
las condiciones.
Así, en Ethereum, cuando "enviamos una cantidad de sender a recipient" estamos literalmente
editando saldos y actualizando el almacenamiento con un conjunto concreto de comandos. Esto sucede
en la cadena y, por lo tanto, una nueva transacción también se crea en la cadena como resultado de este proceso.
En Ergo (como en Bitcoin), las transacciones se crean fuera de la cadena y los nodos de la red solo
las verifican. Los efectos de la transacción en el estado de la blockchain son que las monedas de entrada
(o Cajas en la jerga de Ergo) se eliminan y se añaden cajas de salida al conjunto de
UTXO.
En el ejemplo anterior, no usamos un contrato ErgoScript, sino que asumimos que se utiliza una verificación de firma como
la precondición de gasto. Sin embargo, en escenarios de aplicación más complejos, por supuesto, necesitamos
usar ErgoScript, que es lo que vamos a discutir a continuación.
De Cambiar Estado a Verificar Contexto
En el ejemplo de la función send, primero verificamos la precondición (require(amount <= balances[msg.sender],...)) y luego cambiamos el estado (es decir, actualizamos los saldos
balances[msg.sender] -= amount). Esto es típico en las transacciones de Ethereum. Antes
de cambiar nada, necesitamos verificar si es válido hacerlo.
En Ergo, como discutimos anteriormente, el estado (es decir, el conjunto de cajas UTXO) se cambia implícitamente cuando una
transacción válida se incluye en un bloque. Por lo tanto, solo necesitamos verificar las precondiciones antes de
que la transacción pueda ser añadida al bloque. Esto es lo que hacen los contratos ErgoScript.
No es posible "cambiar el estado" en ErgoScript porque es un lenguaje para verificar
precondiciones para gastar monedas. ErgoScript es un lenguaje puramente funcional sin efectos secundarios que opera sobre valores de datos inmutables. Esto significa que todas las entradas, salidas y
otros parámetros de transacción disponibles en un script son inmutables. Esto, entre otras
cosas, hace que ErgoScript sea un lenguaje muy simple que es fácil de aprender y seguro de usar. Similar a
Bitcoin, cada caja de entrada contiene un script, que debe devolver el valor true para 1) permitir el gasto de la caja (es decir, eliminarla del conjunto UTXO) y 2) añadir la
transacción al bloque.
Si somos pedantes, por lo tanto, es incorrecto (estrictamente hablando) pensar en ErgoScript como el lenguaje de
los contratos Ergo, porque es el lenguaje de proposiciones (predicados lógicos, fórmulas,
etc.) que protegen las cajas de un gasto "ilegal". A diferencia de Bitcoin, en Ergo, toda la
transacción y una parte del contexto actual de la blockchain están disponibles para cada script. Por lo tanto,
cada script puede verificar qué salidas son creadas por la transacción, sus cantidades de ERG y token
(esta capacidad la utilizaremos en nuestros contratos DEX de ejemplo), el número de bloque actual
etc.
En ErgoScript defines las condiciones de si los cambios (es decir, el gasto de monedas) están permitidos
a suceder en un contexto dado. Esto contrasta con programar los cambios
imperativamente en el código de un contrato.
Mientras que el modelo de transacciones de Ergo desbloquea toda una gama de aplicaciones como (DEX, DeFi
Apps, LETS, etc.), diseñar contratos como precondiciones para el gasto de monedas (o scripts de protección)
directamente no es intuitivo. En las siguientes secciones consideraremos una notación gráfica útil
para diseñar contratos de manera declarativa utilizando Diagramas FlowCard, que es una representación visual
de componentes ejecutables (FlowCards).
Las FlowCards tienen como objetivo simplificar radicalmente el desarrollo de dApps en la plataforma Ergo al
proporcionar un lenguaje declarativo de alto nivel, un tiempo de ejecución, un formato de almacenamiento y
a una notación gráfica.
Comenzaremos con un nivel alto de diagramas y bajaremos a la especificación de FlowCard.
Diagramas FlowCard
La idea detrás de los diagramas FlowCard se basa en las siguientes observaciones: 1) Una caja Ergo
es inmutable y solo puede ser gastada en la transacción que la utiliza como entrada. 2) Por lo tanto,
podemos dibujar un flujo de cajas a través de transacciones, de modo que las cajas fluyendo hacia
la transacción se gastan y las que fluyen hacia afuera se crean y se añaden al UTXO. 3) Una
transacción desde esta perspectiva es simplemente un transformador de cajas antiguas a nuevas
preservando los saldos de ERGs y tokens involucrados.
La siguiente figura muestra los elementos principales de la transacción Ergo que ya hemos visto
anteriormente (ahora bajo el nombre de Diagrama FlowCard).

Hay un significado (semántica) estrictamente definido detrás de cada elemento de el diagrama,
para que el diagrama sea una representación visual (o una vista) del componente ejecutable subyacente
(llamado FlowCard).
El FlowCard puede ser utilizado como un componente reutilizable de una dApp Ergo para crear e iniciar la
transacción en la blockchain de Ergo. Discutiremos esto en las próximas secciones.
Ahora veamos las piezas individuales del diagrama FlowCard una por una.
1. Nombre y Parámetros
A cada tarjeta de flujo se le da un nombre y una lista de parámetros tipados. Esto es similar a un
template con parámetros. En la figura anterior podemos ver la tarjeta de flujo Send que tiene cinco parámetros.
Los parámetros se utilizan en la especificación.
2. Billetera del Contrato
Este es un elemento clave de la tarjeta de flujo. Cada caja tiene un script de protección. A menudo es el
script que verifica una firma contra una clave pública. Este script es trivial en ErgoScript
y se define como el template def pk(pubkey: Address) = { pubkey } donde pubkey es
un parámetro del tipo Address. En la figura, el template del script se aplica al
parámetro pk(sender) y así se obtiene un contrato de billetera concreto. Por lo tanto,
pk(sender) y pk(receiver) producen scripts diferentes y representan diferentes billeteras
en el diagrama, aunque utilicen el mismo template.
La Billetera del Contrato contiene un conjunto de todas las cajas UTXO que tienen un script dado derivado del
template de script dado utilizando los parámetros de la tarjeta de flujo. Por ejemplo, en la figura, el
template es pk y el parámetro pubkey se sustituye por el parámetro de la tarjeta de flujo sender.
3. Contrato
Aunque un contrato es una propiedad de una caja, en el diagrama agrupamos las cajas por sus
contratos, por lo tanto, parece que las cajas pertenecen a los contratos, en lugar de que los
contratos pertenezcan a las cajas. En el ejemplo, tenemos tres contratos instanciados
pk(sender), pk(receiver) y minerFee. Nota que pk(sender) es la instanciación del
template pk con el parámetro concreto sender y minerFee es la instanciación del
contrato predefinido que protege las cajas de recompensa del minero.
4. Nombre de la Caja
En el diagrama podemos dar a cada caja un nombre. Además de la legibilidad del diagrama, también usamos el
nombre como un sinónimo de un acceso indexado más complejo a la caja en el contrato. Por
ejemplo, change es el nombre de la caja, que también puede ser utilizado en las condiciones de ErgoScript
en lugar de OUTPUTS(2). También usamos nombres de cajas para asociar condiciones de gasto
con las cajas.
5. Cajas en la billetera
En el diagrama, mostramos cajas (rectángulos más oscuros) como pertenecientes a las billeteras de contrato
(rectángulos más claros). Cada rectángulo de caja está conectado con un rectángulo de transacción
gris mediante flechas naranjas o verdes o ambas. Una caja de salida (con una flecha verde entrante) puede incluir muchas líneas de texto donde cada
línea especifica una condición que debe ser verificada como parte de la transacción. La primera
línea especifica la condición sobre la cantidad de ERG que debe colocarse en la caja. Otras
líneas pueden tomar una de las siguientes formas:
amount: TOKEN- la caja debe contener la cantidad dada de el dadoTOKENR == value- la caja debe contener el valor dado del registro dadoRboxName ? condition- la caja llamadaboxNamedebe verificarconditionen su script.
Discutimos estas condiciones en las secciones a continuación.
6. Cantidad de ERGs en la caja
Cada caja debe almacenar una cantidad mínima de ERGs. Esto se verifica cuando se valida la transacción de creación. En el diagrama, la cantidad de ERGs se muestra siempre como la primera línea (por ejemplo, B: ERG o B - minErg - txFee). La asignación de tipo de valor B: ERG es opcional y puede
usarse para legibilidad. Cuando el valor se da como una fórmula, entonces esta fórmula debe ser
respetada por la transacción que crea la caja.
Es importante entender que variables como amount y txFee no son propiedades nombradas
de las cajas. Son parámetros de todo el diagrama y representan algunas cantidades. O dicho de otra manera, son
parámetros compartidos entre transacciones (por ejemplo, las transacciones de Orden de Venta y Swap del ejemplo DEX a continuación comparten el parámetro tAmt). Así que
el mismo nombre está vinculado al mismo valor en todo el diagrama (aquí es donde la
herramienta ayudaría mucho). Sin embargo, cuando se trata de la validación en la cadena de esos valores,
solo las condiciones explícitas que están marcadas con ? se transforman en ErgoScript. Al mismo tiempo, todas las demás condiciones se aseguran fuera de la cadena durante la construcción de la transacción (por ejemplo, en una
aplicación que utiliza la API de Appkit) y la validación de la transacción cuando se añade a la
blockchain.
7. Cantidad de token T
Una caja puede almacenar valores de muchos tokens. Los tokens en el diagrama están nombrados y una variable value
puede asociarse con el token T utilizando la expresión value: T. El value puede
ser dado por fórmula. Si la fórmula está precedida por un nombre de caja como boxName ? formula,
entonces también debe ser verificada en el script de protección de la caja boxName. Esta
especificación adicional es muy conveniente porque 1) permite validar el diseño visual automáticamente, y 2) las condiciones especificadas en las cajas de un diagrama son suficientes
para sintetizar los scripts de protección necesarios. (más sobre esto
más abajo en "De Diagramas a Contratos ErgoScript")
8. Entradas de Tx
Las entradas están conectadas a la transacción correspondiente mediante flechas naranjas. Una flecha de entrada puede tener una etiqueta de las siguientes formas:
name@index- nombre opcional con un índice es decirfee@0o@2. Esta es una propiedad de
el punto final objetivo de la flecha. El nombre se utiliza en las condiciones de las cajas relacionadas
y elindexes la posición de la caja correspondiente en la colección INPUTS de la
transacción.!action- es una propiedad de la fuente de la flecha y da un nombre para un camino de gasto alternativo de la caja (veremos esto en el ejemplo DEX)
Debido a los caminos de gasto alternativos, una caja puede tener muchas flechas salientes naranjas, en cuyo caso deben estar etiquetadas con diferentes
acciones.
9. Transacción
Una transacción gasta cajas de entrada y crea cajas de salida. Las cajas de entrada son dadas por
todas las flechas naranjas y se espera que las etiquetas coloquen las entradas en los
índices correctos en la colección INPUTS. Las cajas de salida son dadas por las flechas verdes. Cada transacción debe preservar un estricto equilibrio de valores de ERG
(suma de entradas == suma de salidas) y para cada token la suma de entradas >= la suma
de salidas. El diagrama de diseño requiere una especificación explícita de los valores de ERG y token
para todas las cajas de salida para evitar errores implícitos y asegurar una mejor legibilidad.
10. Salidas de Tx
Las salidas están conectadas a la transacción correspondiente mediante flechas verdes. Una flecha de salida puede tener una etiqueta de la siguiente forma name@index, donde un
nombre opcional está acompañado de un índice es decir fee@0 o @2. Esta es una propiedad de la
fuente del punto final de la flecha. El nombre se utiliza en las condiciones de las cajas relacionadas y el
index es la posición de la caja correspondiente en la colección OUTPUTS de la transacción.
Ejemplo: Intercambio Descentralizado (DEX)
Ahora usemos la notación descrita anteriormente para diseñar un FlowCard para una dApp DEX. Es lo suficientemente simple
pero también ilustra todas las características clave de los diagramas FlowCard que hemos introducido en la sección anterior.
El escenario de la dApp se muestra en la figura a continuación:
Hay tres participantes (comprador,
vendedor y DEX) de la dApp DEX y cinco tipos diferentes de transacciones, que son creadas por
los participantes. El comprador quiere intercambiar ergAmt de ERGs por tAmt de tokens TID (o viceversa, el vendedor quiere vender tokens TID por ERGs, quien envía la orden primero no
importa). Tanto el comprador como el vendedor pueden cancelar sus órdenes en cualquier momento. El servicio de emparejamiento fuera de la cadena de DEX
puede encontrar órdenes coincidentes y crear la transacción Swap para completar el intercambio.
El siguiente diagrama especifica completamente (y formalmente) todas las cinco transacciones que deben
ser creadas fuera de la cadena por la dApp DEX. También especifica todas las condiciones de gasto que
se deben verificar en la cadena.

Discutamos el diagrama FlowCard y la lógica de cada transacción en detalle:
Transacción de Orden de Compra
Un comprador crea una transacción de Orden de Compra. La transacción gasta E cantidad de ERGs
(que escribiremos E: ERG) de una o más cajas en la billetera pk(buyer). La
transacción crea una caja bid con ergAmt: ERG protegida por el script buyOrder. El
script buyOrder se sintetiza a partir de la especificación (ver
más abajo en "De Diagramas a Contratos ErgoScript") ya sea manualmente o automáticamente por una
herramienta. Aunque no necesitamos definir el script buyOrder explícitamente durante
diseño, en tiempo de ejecución la caja bid debe contener el script buyOrder como la
proposición de protección (que verifica las condiciones de gasto de la caja), de lo contrario, las condiciones
especificadas en el diagrama no serán verificadas.
La caja change se crea para equilibrar las sumas de entrada y salida de la transacción.
La caja de tarifa de transacción se omite porque puede ser añadida automáticamente por las herramientas. En
práctica, sin embargo, el diseñador puede añadir la caja de tarifa explícitamente a un diagrama. Cubre
los casos de transacciones más complejas (como Swap) donde hay muchas formas de pagar
la tarifa de transacción.
Cancelar Compras, Cancelar Ventas
En cualquier momento, el comprador puede cancelar la orden enviando la transacción CancelBuy. La
transacción debe satisfacer el contrato de protección buyOrder que protege la caja bid.
Como puedes ver en el diagrama, tanto las transacciones Cancel como Swap pueden gastar la
caja bid. Cuando una caja tiene alternativas de gasto (o caminos de gasto), cada
alternativa se identifica con un nombre único precedido por ! (!cancel y !swap
para la caja bid). Cada camino alternativo tiene condiciones de gasto específicas. En nuestro ejemplo,
cuando la transacción Cancel Buy gasta la caja bid, la condición ?buyer debe ser
satisfecha, que leemos como "la firma para la dirección buyer debe ser presentada en la
transacción". Por lo tanto, solo el comprador puede cancelar la orden de compra. Esta condición de "firma"
es solo requerida para el camino de gasto alternativo !cancel y no es requerida para !swap.
Transacción de Orden de Venta
La transacción de Orden de Venta es similar a la BuyOrder en que trata con tokens además de ERGs. La transacción gasta E: ERG y T: TID tokens de la billetera del vendedor
(especificada como contrato pk(seller)). Las dos salidas son ask y change. El cambio
es una caja estándar para equilibrar la transacción. La caja ask mantiene tAmt: TID tokens para el intercambio
y minErg: ERG - la cantidad mínima de ERGs requeridos en cada caja.
Transacción de Intercambio
Esta es una transacción clave en el escenario de la dApp DEX. La transacción tiene varias condiciones de gasto
sobre las cajas de entrada y esas condiciones están incluidas en los scripts buyOrder y
sellOrder (que son verificadas cuando la transacción se añade a la
blockchain). Sin embargo, en el diagrama, esas condiciones no están especificadas en las cajas bid y
ask, sino que están definidas en las cajas de salida de la transacción.
Esta es una convención para mejorar la usabilidad porque la mayoría de las condiciones se relacionan con las propiedades de
las cajas de salida. Podríamos especificar esas propiedades en la caja bid, pero entonces tendríamos
que usar expresiones más complejas.
Consideremos la salida creada por la flecha etiquetada con buyerOut@0. Esta etiqueta nos dice
que la salida está en el índice 0 en la colección OUTPUTS de la transacción y
que en el diagrama podemos referirnos a esta caja por el nombre buyerOut. Así que podemos etiquetar
tanto la caja misma como la flecha para darle un nombre a la caja.
Las condiciones mostradas en la caja buyerOut tienen la forma bid ? condition, lo que significa
que deben ser verificadas en la cadena para gastar la caja bid.
Las condiciones tienen el siguiente significado:
tAmt: TIDrequiere que la caja tengatAmtcantidad de tokenTIDR4 == bid.idrequiere que el registro R4 en la caja sea igual al id de la cajabid.script == buyerrequiere que la cajabuyerOuttenga el script de la billetera donde se
encuentra en el diagrama, es decir,pk(buyer)
Propiedades similares se añaden a la caja sellerOut, que se especifica para estar en el índice 1
y se le da un nombre utilizando la etiqueta en la caja misma, en lugar de en la flecha.
La transacción Swap gasta dos cajas bid y ask utilizando el camino de gasto !swap en ambas,
sin embargo, a diferencia de !cancel, las condiciones en el camino no están especificadas. Aquí es donde los
prefijos bid ? y ask ? entran en juego. Se utilizan para que las condiciones listadas en las cajas buyerOut y
sellerOut se muevan al camino de gasto !swap de las cajas bid y ask respectivamente.
Si miras las condiciones de las cajas de salida, verás que especifican exactamente el intercambio de valores entre las billeteras del vendedor y del comprador. El comprador recibe la cantidad necesaria
de token TID y el vendedor recibe la cantidad correspondiente de ERGs. La transacción Swap
es creada cuando hay dos cajas coincidentes con contratos buyOrder y sellOrder.
De Diagramas a Contratos ErgoScript
Lo interesante de las especificaciones de FlowCard es que podemos usarlas para generar automáticamente
los scripts necesarios de ErgoTree.
Con el soporte de herramientas apropiadas, esto puede hacerse automáticamente, pero en ausencia de
esto, puede hacerse manualmente. Así, el FlowCard nos permite capturar y representar visualmente
todas las elecciones de diseño y detalles semánticos de una dApp Ergo.
Lo que vamos a hacer a continuación es crear mecánicamente el contrato buyOrder a partir de la
información dada en la tarjeta de flujo DEX.
Recuerda que cada script es una proposición (expresión de valor booleano) que debe evaluarse
a true para permitir el gasto de la caja. Cuando tenemos muchas condiciones que deben cumplirse al mismo
tiempo, podemos combinarlas en una fórmula lógica utilizando la operación binaria AND, y si tenemos
alternativas (no necesariamente exclusivas) podemos ponerlas en la operación OR.
La caja buyOrder tiene los caminos de gasto alternativos !cancel y !swap. Por lo tanto, el
código ErgoScript debe tener una operación OR con dos argumentos: uno para cada camino de gasto.
/** contrato buyOrder */
{
val cancelCondition = {}
val swapCondition = {}
cancelCondition || swapCondition
}
La fórmula para la expresión cancelCondition se da en el camino de gasto !cancel
de la caja buyOrder. Podemos incluirla directamente en el script.
/** contrato buyOrder */
{
val cancelCondition = { buyer }
val swapCondition = {}
cancelCondition || swapCondition
}
Para el camino de gasto !swap de la caja buyOrder, las condiciones están especificadas en la
caja de salida buyerOut de la transacción Swap. Si simplemente las incluimos en el
swapCondition, obtendremos un script sintácticamente incorrecto.
/** contrato buyOrder */
{
val cancelCondition = { buyer }
val swapCondition = {
tAmt: TID &&
R4 == bid.id &&
@contract
}
cancelCondition || swapCondition
}
Sin embargo, podemos traducir las condiciones de la sintaxis del diagrama a expresiones ErgoScript
usando las siguientes reglas simples:
buyerOut@0==>val buyerOut = OUTPUTS(0)tAmt: TID==>tid._2 == tAmtdondetid = buyerOut.tokens(TID)R4 == bid.id==>R4 == SELF.iddondeR4 = buyerOut.R4[Coll[Byte]].getscript == buyer==>buyerOut.propositionBytes == buyer.propBytes
Nota, en el diagrama TID representa un id de token, pero ErgoScript no tiene acceso a los
tokens por los ids, así que no podemos escribir tokens.getByKey(TID). Por esta razón, cuando el
diagrama se traduce a ErgoScript, TID se convierte en una constante nombrada del índice en
la colección de tokens de la caja. El valor concreto de la constante se asigna cuando se crea la
transacción BuyOrder con la caja buyOrder. La correspondencia y
consistencia entre el tokenId real, la constante TID y los tokens reales de la
caja buyerOut se asegura por el código de la aplicación fuera de la cadena, que es completamente
posible ya que todas las transacciones son creadas por la aplicación utilizando FlowCard como una
especificación guía. Esto puede sonar demasiado complicado, pero esto es parte de la traducción
de la especificación del diagrama al código de aplicación ejecutable real, la mayor parte de la cual puede ser
automatizada.
Después de la transformación, podemos obtener un script correcto que verifica todas las precondiciones
requeridas para gastar la caja buyOrder.
/** contrato buyOrder */
def DEX(buyer: Addrss, seller: Address, TID: Int, ergAmt: Long, tAmt: Long)
{
val cancelCondition: SigmaProp = { buyer } // verificar la firma del comprador (ProveDlog)
val swapCondition = OUTPUTS.size > 0 && { // asegurando acceso a OUTPUTS
val buyerOut = OUTPUTS(0) // de buyerOut@0
buyerOut.tokens.size > TID && { // asegurando acceso a tokens
val tid = buyerOut.tokens(TID)
val regR4 = buyerOut.R4[Coll[Byte]]
regR4.isDefined && { // asegurando acceso a R4
val R4 = regR4.get
tid._2 == tAmt && // de tAmt: TID
R4 == SELF.id && // de R4 == bid.id
buyerOut.propositionBytes == buyer.propBytes // de script == buyer
}
}
}
cancelCondition || swapCondition
}
Un script similar para la caja sellOrder puede obtenerse utilizando las mismas reglas de traducción.
Con la ayuda de las herramientas, el código de los contratos puede generarse mecánicamente a partir de la
especificación del diagrama.
Conclusiones
Los modelos de programación declarativa ya han ganado la batalla contra la programación imperativa
en muchos dominios de aplicación como Big Data, Procesamiento de Flujos, Aprendizaje Profundo, Bases de Datos,
etc. Ergo está pionero en el modelo declarativo de desarrollo de dApps como una alternativa mejor y más segura
al ahora popular modelo imperativo de contratos inteligentes.
El concepto de FlowCard desplaza el enfoque de escribir contratos ErgoScript al
gran flujo de valores (de ahí el nombre), de tal manera que ErgoScript siempre
puede generarse a partir de ellos. Nunca necesitarás mirar el código de ErgoScript una vez que las herramientas
estén en su lugar.
Aquí están los posibles próximos pasos para el trabajo futuro:
-
Formato de almacenamiento para la Especificación FlowCard y el correspondiente formato de archivo estandarizado EIP
(Json/XML/Protobuf). Esto permitirá a varias herramientas (Editor de Diagramas, Tiempo de Ejecución, dApps, etc.)
crear y usar archivos*.flowcard. -
Visor de FlowCard, que puede generar los diagramas a partir de archivos
*.flowcard. -
Tiempo de Ejecución de FlowCard, que puede ejecutar archivos
*.flowcard, crear y enviar transacciones a la red Ergo. -
Herramienta de Diseño de FlowCard, que puede simplificar el desarrollo de diagramas complejos. Esto hará
que el diseño y la validación de contratos Ergo sean una experiencia agradable, más como dibujar
que programar. Además, la corrección de todo el escenario de dApp puede ser
verificada y controlada por las herramientas.
Referencias
Share post
13 de agosto de 2025
12 de agosto de 2025
9 de julio de 2025
12 de mayo de 2025

9 de febrero de 2022

8 de febrero de 2022

5 de febrero de 2022

1 de febrero de 2022

27 de enero de 2022

20 de enero de 2022

18 de enero de 2022

6 de enero de 2022

4 de enero de 2022

30 de diciembre de 2021

28 de diciembre de 2021

23 de diciembre de 2021








