Especificación de Scilla en Maude Specification of Scilla in Maude - Trabajo de Fin de Máster Curso 2020-2021 Autor - E-Prints Complutense

Página creada Sara Álvarez
 
SEGUIR LEYENDO
Especificación de Scilla en Maude
Specification of Scilla in Maude

   Trabajo de Fin de Máster
       Curso 2020–2021

              Autor
     Laura de la Fuente Lorenzo

             Director
      Adrián Riesco Rodríguez

  Máster en Ingeniería Informática
       Facultad de Informática
 Universidad Complutense de Madrid
Especificación de Scilla en Maude
 Specification of Scilla in Maude

 Trabajo de Fin de Máster en Ingeniería Informática
Departamento de Sistemas Informáticos y Computación

                      Autor
            Laura de la Fuente Lorenzo

                     Director
              Adrián Riesco Rodríguez

           Convocatoria: Junio/Julio 2021
                 Calificación: 7,5

         Máster en Ingeniería Informática
              Facultad de Informática
        Universidad Complutense de Madrid

                13 de julio de 2021
Resumen

Especificación de Scilla en Maude

    Este trabajo busca proporcionar una especificación formal al lenguaje de contratos
inteligentes Scilla mediante el lenguaje Maude, para así poder ejecutar contratos reales
siempre que estén escritos adecuadamente y realizar un análisis para detectar partes de
código muerto, es decir, variables que no van a ser usadas en ningún momento. Para llevar a
cabo esta implementación, se ha tenido que realizar una transformación previa del archivo
de entrada por ciertas limitaciones que tiene Maude, se han creado dos gramáticas: la
primera, muy parecida a la sintaxis de Scilla, se utiliza para analizar sintácticamente el
archivo de entrada; por su lado, la segunda gramática es más funcional y se utliza para
trabajar internamente. Para la descripción de la semántica se ha creado una memoria
para poder almacenar las variables con los valores y el nombre, entradas y cuerpo de los
procedimientos, funciones y transiciones de Scilla, y distintas reglas de reescritura que
permiten actualizar esos datos en la memoria y ejecutar las operaciones.
    También, el usuario podrá interactuar con el sistema por medio de la entrada/salida,
donde puede introducir un contrato válido, indicar si quiere analizarlo o ejecutarlo y en
el caso de que se quiera ejecutar, introducir los valores necesarios para ello. También
recibirá respuesta por parte del sistema, en el caso del análisis, indicando si el contrato es
correcto o si tiene partes que no se usan, mostrando cuáles son y a qué función, transición
o procedimiento corresponde y, en el caso de la ejecución, devolviendo el estado de las
variables que son globales y además, la información de la transición ejecutada.
    Por último, se busca también que el análisis sea escalable para no solo analizar contratos
escritos en Scilla, sino en cualquier otro lenguaje. Para ello, se va ha parametrizado el
código, es decir, en vez de poner la sintaxis concreta de las partes que se quieren analizar
de Scilla dentro del código que realiza el análisis, se especifica aparte y se pasa como
parámetro.

Palabras clave

Maude, Scilla, lógica de reescritura, contratos inteligentes, metarrepresentación, especifi-
cación formal.

                                                                                            v
Abstract

Specification of Scilla in Maude

    The present project seeks to provide a formal specification to the Scilla smart contract
language using the Maude language, in order to be able to execute real contracts, as long as
they are properly written, and to perform an analysis in order to detect parts of dead code,
specifically variables that are not going to be used at any time. In order to implement it,
the input file had to be previously converted due to certain limitations of Maude. Thus,
two grammars have been created. On the one hand, the first one, very similar to Scilla
syntax, is used to syntactically analyze the input file. On the other hand, the second
grammar is more functional and is used to work internally. For the description of the
semantics, a memory has been created to save the variables that include the values, the
names, the entries and the body of Scilla procedures, functions and transitions, as well as
different rewrite-rules that allow updating these data in the memory and implementing the
operations.
    In addition, the users can interact with the system through input/output, where they
can enter a valid contract, indicate whether they want to analyze or execute it and, in
that case, enter the necessary values for it. The users will also receive a response from the
system, in the case of analysis, indicating if the contract is correct or if it has modules
that are not used, showing which ones are and to which function, transition or procedure
they correspond, and, in the case of execution, returning the status of the variables that
are global, as well as the information of the transition executed.
    Finally, the analysis is also intended to be scalable to not only analyze contracts written
in Scilla language, but also in any other language. For this purpose, the code has been
parameterized, that is, instead of introducing the specific syntax of the Scilla modules to be
analyzed within the code that performs the analysis, it is specified separately and passed
as a parameter.

Keywords

Maude, Scilla, rewriting logic, smart contracts, metarepresentation, formal specification.

                                                                                           vii
Índice

1. Introducción                                                                                  1
  1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .        1
  1.2. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       2
  1.3. Plan de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       2

2. Preliminares                                                                                  5
  2.1. Maude . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       5
  2.2. Contratos inteligentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .        8
       2.2.1. Solidity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       9
  2.3. Scilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    9
  2.4. Trabajo relacionado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3. Descripción del Trabajo                                                                      19
  3.1. Entrada/Salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
  3.2. Preparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
  3.3. Metaparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
  3.4. Parse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
  3.5. Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
  3.6. Pedir, cargar parámetros y ejecutar transición . . . . . . . . . . . . . . . . . 27
  3.7. Análisis de código muerto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4. Pruebas                                                                                      31

5. Conclusiones y Trabajo Futuro                                                                45

6. Introduction                                                                                 47
  6.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

                                                                                                ix
6.2. Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
  6.3. Work plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7. Conclusions and Future Work                                                             51
Índice de figuras

 1.1. Diagrama de Gantt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   3

 2.1. Ejemplo de un contrato escrito en Solidity ([Sol20]) . . . . . . . . . . . . . . 10
 2.2. Estructura básica de un contrato de Scilla ([Sci19]) . . . . . . . . . . . . . . 11
 2.3. Fallos en distintos contratos y sus pérdidas [HSR+ 18]. . . . . . . . . . . . . 16

 3.1. Representación del flujo que sigue el sistema internamente . . . . . . . . . . 20
 3.2. Código de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
 3.3. Representación del flujo que sigue la Entrada/Salida . . . . . . . . . . . . . 22
 3.4. Código de ejemplo tras la transformación a Qids . . . . . . . . . . . . . . . 23
 3.5. Código de ejemplo tras el preparse . . . . . . . . . . . . . . . . . . . . . . . 24
 3.6. Código de ejemplo tras el metaparse . . . . . . . . . . . . . . . . . . . . . . 25
 3.7. Código de ejemplo tras el parse . . . . . . . . . . . . . . . . . . . . . . . . . 26
 3.8. Código de ejemplo tras guardar en memoria . . . . . . . . . . . . . . . . . . 27
 3.9. Código de ejemplo tras la ejecución . . . . . . . . . . . . . . . . . . . . . . . 28

 4.1. Diagrama de flujo sobre la ejecución del sistema . . . . . . . . . . . . . . . . 31

 6.1. Gantt diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

                                                                                          xi
Índice de tablas

 2.1. Comparación entre K-framework y compiladores conocidos ([MR13]) . . . . 16

 3.1. Tabla resumen sobre los archivos, los módulos y una descripción de su con-
      tenido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

                                                                                         xiii
Capı́tulo    1
Introducción

    En este trabajo se desarrolla una especificación en lógica de reescritura del lenguaje de
contratos inteligentes Scilla usando Maude. A partir de esta especificación, se desea que
el usuario pueda ejecutar cualquier contrato escrito en Scilla, introduciendo de manera
interactiva los parámetros necesarios para ello. Por otro lado, se ha querido realizar un
análisis sencillo para detectar partes de código no utilizados y así poder generar contratos
más eficientes.
    A continuación, se explica la motivación de este trabajo, exponiendo también sus obje-
tivos y, por último, se añade un apartado sobre cuál ha sido el plan de trabajo, detallando
las partes que tiene, indicando el orden en el que se han realizado e ilustrando el proceso
con un diagrama de Gantt donde se muestra la duración en días de cada una de esas tareas.

1.1.    Motivación

    En los últimos años, gracias al avance de la informática, se ha ido automatizando
distintos aspectos de la vida cotidiana que antes era impensable realizar.
    Hace pocos años, tras la aparición de las monedas viruales como el Bitcoin [Fra15] y
de las bases de datos distribuidas, surgió la idea de los contratos inteligentes para poder
realizar transacciones de dinero o acuerdos de cualquier tipo de una forma segura y sin que
interfieran terceras personas, como por ejemplo, los abogados o los bancos. Hoy en día hay
varios lenguajes que implementan esta idea, como es el caso de Solidity y Scilla.
    La importante función que ejercen estos contratos obliga a que no puedan cometer fallos
y a que sean seguros. Por esto mismo, es importante detectar los errores que puedan tener
rápidamente para así evitar consecuencias graves. En este sentido, tener una especificación
formal de estos lenguajes nos permitiría analizarlos y ver los posibles errores que puedan
tener antes de que sean desplegados.
    Por otro lado, uno de los grandes inconvenientes de estos contratos es que son difíciles
de entender para personas sin experiencia en programación, por lo que pueden seguir siendo
necesarias terceras personas que “traduzcan” el significado de los contratos. Para resolver
este problema, Scilla es un lenguaje sencillo y de fácil aprendizaje que sacrifica parte de
la expresividad de otros lenguajes como Solidity a cambio de legibilidad, por lo que es un
buen candidato para usarse de manera frecuente y por público no especializado. Por esta

                                                                                           1
2                                                                 Capítulo 1. Introducción

razón, y porque no hay demasiados estudios sobre él, se ha seleccionado este lenguaje para
desarrollar una especificación formal que pueda servir como base para realizar posteriores
análisis que permitan su verificación.

1.2.      Objetivos

    Los objetivos principales de este trabajo son, por un lado, proporcionar una semántica
formal al lenguaje de contratos inteligentes Scilla para, posteriormente, poder realizar
diversos análisis sobre él. A modo ilustrativo, en este trabajo se ha implementado uno
que consiste en ver si hay partes del código que no sean utilizadas. Gracias a ello se puede
obtener un código más eficiente, ya que al saber qué partes de código hay sin usar, se puede
evitar realizar operaciones innecesarias que consumen tiempo y además, ocupan espacio
en memoria. Este análisis se desea que sea escalable para así permitir que, además de
proporcionar esta información sobre contratos en Scilla, se pueda extender para cualquier
otro lenguaje con cambios mínimos.
    Aparte de permitir realizar este análisis, el sistema tiene que ser capaz de interactuar
con el usuario y de ejecutar un contrato real válido, es decir, que esté bien escrito sintác-
ticamente y, además, que siga exactamente la sintaxis real de Scilla, lo que requiere de un
análisis previo por ciertas limitaciones que tiene Maude, como se verá más adelante.

1.3.      Plan de trabajo

    A continuación enumeramos las tareas realizadas durante el desarrollo:

    1. Estudio del lenguaje Scilla desde cero, analizando su sintaxis y posteriormente, viendo
       ejemplos sobre estos contratos y sus ejecuciones.

    2. Repasar el lenguaje Maude y estudiar nuevos aspectos que no se habían visto ante-
       riormente, como es el caso de la meta-representación y la entrada/salida.

    3. Implementar la gramática del lenguaje sin meta-representación. En un primer mo-
       mento se consideró en diseñar esta gramática usando el tipo String para las variables
       en vez de usar los tokens genéricos. Esta idea se descartó porque requería modificar
       los contratos reales.

    4. Implementar la primera gramática del lenguaje con meta-representación, basándose
       en la primera gramática que se ha generado en el apartado anterior.

    5. Realizar el análisis sintáctico del lenguaje.

    6. Implementar la segunda gramática, es decir, la gramática interna para resolver los
       tokens a la hora de usar las reglas.

    7. Diseñar la memoria donde se van a guardar las variables, transiciones y procedimien-
       tos.

    8. Implementar las reglas que van a guardar los datos en la memoria, en este caso,
       en cada una de las reglas se tiene el contrato parseado, una memoria para tener las
       variables que son globales, las funcionesy procedimientos y otra para las transiciones.
1.3. Plan de trabajo                                                                         3

  9. Implementar las reglas que realizarán la ejecución del contrato. En este caso solo se
     tienen las dos memorias y se realiza la ejecución sobre ellas.

 10. Implementar las reglas a cargo de entrada/salida para que el usuario pueda inter-
     actuar con el sistema, permitiéndole cargar un archivo que contenga un contrato de
     Scilla, indicar si quiere realizar un análisis o ejecutarlo y, en el caso de la ejecución,
     introducir los parámetros necesarios.

 11. Implementar el preparse, creado para modificar el archivo de entrada para eliminar
     ambigüedades y caracteres no soportados por Maude.

 12. Realizar el análisis del código muerto, es decir, código declarado pero no utilizado.

    Una vez visto el orden en el que tiene lugar cada una de las tareas, a continuación,
en la figura 1.1 se muestra un diagrama de Gantt con las tareas en el mismo orden y la
duración en días de cada una de ellas.

                              Figura 1.1: Diagrama de Gantt

   El resto de la memoria se organiza como sigue:

     En el capítulo 2 se explica los lenguajes usados y los estudios o proyectos que se han
     realizado con anterioridad y están relacionados con este trabajo.

     En el capítulo 3 se verá a fondo cómo se ha desarrollado el trabajo, explicando las
     partes que contiene y cómo se ha implementado cada una de ellas.

     En el capítulo 4 se va a ver cómo funciona el sistema desarrollado y se pondrán unos
     ejemplos de contratos de Scilla para ilustrar su funcionamiento.

     En el capítulo 5 se desarrollan las conclusiones de este trabajo y las distintas líneas
     de trabajo que se puede desarrollar a partir de él.
4                                                              Capítulo 1. Introducción

   El código fuente de la aplicación está disponible en https://github.com/laudf/Especificacion-
y-verificacion-de-Scilla-en-Maude bajo licencia MIT.
Capı́tulo    2
Preliminares

    Como ya se ha mencionado en la introducción, este proyecto consiste en la implementa-
ción del lenguaje de programación de contratos inteligentes Scilla utilizando el lenguaje de
especificación Maude. Para ello, en esta sección se presentará brevemente Maude. Asimis-
mo, se realizará una breve introducción a los contratos inteligentes, explicando a fondo el
lenguaje Scilla y comparándolo con otros lenguajes de este tipo como Solidity. Por último,
se tratará el trabajo relacionado, incluyendo los distintos casos de éxito para lenguajes
como Java y C y la creación de la KEVM para Ethereum.

2.1.     Maude

    Maude [CDE+ 07] es un lenguaje de programación basado en lógica de reescritura que
usa lógica ecuacional para definir las estructuras de datos y reglas de reescritura para
definir el comportamiento dinámico de los sistemas. Según el artículo [MR06], la lógica de
reescritura surgió de la necesidad de unificar dos tipos distintos de marcos semánticos para
lenguajes de programación. Por un lado, se tiene la semántica ecuacional, cuyas definiciones
formales tienen la forma de ecuaciones, y que es adecuada para sistemas deterministas.
Por otro lado, está la semántica operacional estructurada, donde las definiciones son reglas
que proporcionan una descripción paso a paso de la ejecución del programa, por lo que
este método prevalece en los lenguajes concurrentes. Como cada uno de los dos métodos
comentados tienen sus ventajas e inconvenientes, se ha decidió unifircarlos en un marco
único, la llamada lógica de reescritura.
    Maude es un lenguaje de más alto nivel que otros lenguajes imperativos, lo que permite
resolver problemas complejos de una manera más sencilla que lenguajes como C o Java.
Además, sus fundamentos matemáticos permiten razonar sobre los sistemas que definen. Y
no solo eso, también proporciona una implementación muy eficiente de un comprobador de
modelos para lógica lineal temporal que permite verificar si los sistemas satisfacen ciertas
propiedades y, en el caso de no verificarlas, es capaz de mostrar contraejemplos para ellas.
  A continuación, se presenta la sintáxis básica de Maude. Las unidades básicas son los
módulos, de los que encontramos dos tipos:

       Módulos funcionales: aquí se definen los tipos de datos y las operaciones y cuya
       sintaxis es la siguiente:

                                                                                          5
6                                                                  Capítulo 2. Preliminares

             fmod  is
                 
             endfm

        Los tipos se declaran con el comando sort, los subtipos con subsort y los operado-
        res con op. Además, los operadores pueden tener distintas propiedades como pueden
        ser la asociatividad (assoc), conmutatividad (comm), idempotencia (idem), elemento
        identidad (id) y constructor (ctor). Para declarar las operaciones se usa el comando
        eq y para las variables se usa var o vars si es una variable o varias respectivamente.
        Un ejemplo sencillo sería:

             fmod Naturales is
                 sort Nat .
                 op 0 : -> Nat [ctor] .
                 op sig : Nat -> Nat [ctor] .
                 op suma : Nat Nat -> Nat [ctor comm assoc] .
                 vars N N1 : Nat .
                 eq suma(0, N) = N .
                 eq suma(sig(N), N1) = sig(suma(N, N1)) .
             endfm

        Donde se define el módulo de los naturales, se declara el tipo Nat y se especifica la
        operación suma.
        También se pueden importar otros módulos predeterminados o que se hayan creado
        con anterioridad. En este caso solo se usará la palabra reservada pr, que indica que
        el módulo a continuación se importa en modo protecting, por lo que no se podrán
        añadir nuevos constructores ni identificar términos que antes fuesen diferentes.

        Módulos del sistema: en este caso se especifica una teoría de reescritura, es decir, se
        definen reglas de reescritura. Su estructura es parecida a la de los módulos explicados
        anteriormente, pero en vez de usar fmod y endfm se usa mod y endm. Las reglas sirven
        para cambiar de estado y permite ejecutar el sistema que se haya definido y su sintaxis
        es:
        rl [estado1] => [estado2] ..

      A continuación presentamos algunos módulos predefinidos útiles en este trabajo:

    BOOL: este módulo se importa por defecto. Define el tipo Bool con dos contructoras,
        true y false, y varias funciones como igualdad sintáctica (_==_), negación (not_),
        conjunción lógica (_and_) y disyunción (_or_).

    NAT: este módulo define los tipos Zero, que incluye al número 0, NzNat, que identifica
        los números naturales sin incluir el 0, y Nat, que es la unión de los tipos anteriores.
        Define también operaciones propias de los números naturales, como la suma (_+_) o
        la diferencia simétrica (sd(_,_)).

    INT: define el tipo Int, que se corresponde con los números enteros, y define operaciones
         propias de los enteros, como la resta (_-_).
2.1. Maude                                                                                7

 STRING: define el tipo String, que engloba las cadenas de caracteres delimitadas por
     comillas dobles.

 QID: define el tipo Qid, que englobalas cadena de caracteres precedida por ’.

    Maude también tiene la opción de interactuar con el usuario mediante entrada/salida.
Los flujos estándar en Maude son los mismos que para cualquier proceso Unix: entrada
estándar (stdin), salida estándar (stdout) y el error estándar (stderr). Estos flujos en
Maude se corresponden con tres objetos externos, definidos en un módulo predefinido
llamado STD-STREAM en el archivo file.maude. Hay distintas operaciones que utilizan
estos objetos y son las que se usarán para interactuar con el usuario:

     getLine solicita al usuario información y su sintaxis es:
     getLine(stdin, , ).
     Este mensaje siempre recibe como respuesta un mensaje gotLine que indica que el
     usuario ya ha introducido la información solicitada, cuya sintaxis es:
     gotLine(, stdin, )

     write muestra un mensaje por la salida estándar. Su sintaxis es:
     write(stdout, , )
     Este mensaje siempre recibe un mensaje wrote como respuesta para indicar que ya
     ha finalizado la escritura.

    Además de los flujos estándar, Maude también implementa entrada/salida mediante
ficheros de texto. Para ello, se tiene un objeto externo llamado fileManager que funciona
como administrador. Para abrir un archivo se usa el mensaje openFile(fileManager,
, , ), donde se solicita al administrador que abra
el archivo . Los permisos pueden ser de escritura ("w"), lectura ("r") o
ambos ("a"). Si se abre el archivo, se devuelve un mensaje openedFile con un identificador
del archivo que se ha abierto. En caso de error se devuelve un mensaje fileError con un
texto explicativo de por qué no se ha podido abrir el archivo correctamente.
    Gracias a que la lógica de reescritura es reflexiva, Maude implementa un módulo
META-LEVEL que permite usar términos y módulos de Maude como datos normales. Es
importante en este trabajo entender cómo se meta-representan términos: la idea principal
es usar notación prefija, precediendo cada operador por una comilla (’) e incluyendo los
argumentos, meta-representados, entre corchetes y separados por comas. Las constantes
siguen esta notación, incluyendo su tipo con un punto, mientras que las variables usan dos
puntos. Por ejemplo, la meta-representación de x + 1 sería ’_+_[’x:Nat,’s_[’0.Zero]],
donde asumimos que x tiene el tipo Nat.

   Presentamos a continuación algunas funciones útiles del módulo META-LEVEL:

     metaRewrite reescribe un término usando tanto las ecuaciones como las reglas, a
     imitación del comando rewrite del nivel objeto. Tiene tres parámetros: la meta-
     representación del módulo en el que se ejecuta el comando, la del término que será
     reescrito y un límite superior en el número de reescrituras. Esta función devuelve el
     término resultado y su tipo si la reescritura tiene éxito y un error en caso contrario.

     metaParse se encarga de devolver la meta-representación de un término. Recibe un
     módulo donde se tienen definidas las ecuaciones y una lista de Qids que se usarán
8                                                                 Capítulo 2. Preliminares

       para construir el término. Esta función devuelve un término y su tipo en caso de que
       la lista de Qid se corresponda con un término correcto en el módulo y un error en
       otro caso.

   Por último, se introducirán los concepto básicos de programación parametrizada en
Maude, es decir, introducir parámetros como argumentos a los módulos. Esto ayuda a
conseguir un código escalable. Posee los siguientes bloques básicos:

       Teorías: en ellas se declaran los requisitos que tienen que satisfacer los parámetros
       con los que se va a instanciar el módulo parametrizado. Su sintaxis es similar a los
       módulos, pero en vez de usar mod y endm usaremos fth y endfth.
       Módulos parametrizados por una serie de teorías. En estos módulos se pueden usar
       las funciones y los tipos de las teorías, aunque todavía no estén instanciados a valores
       concretos.
       Vistas: se usan para especificar cómo un cierto módulo satisface una teoría ya creada.
       Su sintaxis es:

           view  from  to  is
                
           endv

       Módulos instanciados, que usan las vistas para concretar las teorías de los módulos
       parametrizados.

2.2.     Contratos inteligentes

    Los contratos inteligentes [FP19] son programas informáticos centrados en ejecutar
acuerdos entre dos partes. Estos contratos pueden ser llamados tanto por personas físicas o
jurídicas como por máquinas o por otros programas, de ahí que no necesiten la intervención
humana para ejecutarse.
    En los años 90 ya se empezó a pensar en este tipo de contratos: un criptólogo llamado
Nick Szabo [BBV] quería desarrollar unos protocolos informáticos que permitieran sustituir
a los abogados y los correspondientes trámites legales que conlleva realizar un acuerdo entre
dos o más partes. Sin embargo, en este momento era algo impensable ya que no había una
plataforma segura donde almacenarlos. Esto cambió a partir de la llegada de las bases de
datos descentralizadas, en particular con la cadena de bloques (blockchain) [Bas17] como
ejemplo más popular.
    Estos contratos inteligentes tienen muchas aplicaciones en el mundo real como, por
ejemplo, la automatización de pagos, asegurándose así que llega la cantidad que se pide
en el momento y a la persona que corresponde; cambios de propiedad si el registro está
almacenado en la blockchain; transacciones energéticas; propiedad intelectual, sirviendo en
este caso, para cumplir determinados acuerdos sobre las licencias de propiedad intelectual
o facilitar el pago a los titulares de ella; seguros; apuestas; compras automáticas y vota-
ciones, entre otros muchos. Con estas premisas pueden surgir varias preguntas: ¿Cómo se
actualizan los contratos inteligentes si necesitan recibir información del exterior? Esto pue-
de pasar en el caso de las apuestas deportivas que se necesita saber qué equipo ha ganado,
2.3. Scilla                                                                                 9

o si hay dos personas implicadas en el mismo fin, por ejemplo, ahorrar una cantidad de
dinero. Además, ¿quién asegura que la otra persona no lo va a sacar sin contar con él?
    Como solución para la primera pregunta contamos con unas herramientas informáticas
llamadas oráculos (oracle) que permiten actualizar el estado de un contrato aportándole
información del exterior.
    Para contestar la segunda consideramos una función, llamada multifirma, que consiste
en obligar a aceptar la transición a todas las partes del contrato para que se pueda ejecutar.
En resumen, estos contratos tienen tanto ventajas como inconvenientes.
    Por otro lado, estos contratos pueden tener diversos problemas en cuanto a seguridad
se refiere, como ataques de denegación de servicio (DoS) por la realización de cálculos
infintos. Para solucionar esto, hay un elemento que tienen todos estos contratos llamado
“gas”, que es una unidad que mide el esfuerzo, en cuanto computación se refiere, que tiene
ejecutar una determinada operación. Cuando se ejecuta una transición hay una cantidad
de gas disponible y va disminuyendo a medida que se van ejecutando operaciones, por lo
que si la transición se queda sin gas, no puede seguir ejecutándose y salta una excepción
por falta de gas, por lo que nunca se podrán tener cálculos infinitos.
    Por último, estos contratos tienen algunas ventajas: aportan autonomía, seguridad y
confianza. Pero también tiene algunos inconvenientes, ya que no pueden ser útiles en todos
los ámbitos de la vida: no hay regulación legal para ellos. Pero a pesar de este inconveniente
la sociedad está evolucionando y cada vez más gente esta haciendo uso de estas tecnologías.
    A continuación, se comentarán diversos lenguajes de contratos inteligentes.

2.2.1.    Solidity

    Solidity [Mod18, Sol21a] es un lenguaje de programación de alto nivel orientado a ob-
jetos para programar contratos inteligentes. Este lenguaje es usado por varias plataformas
de cadenas de bloques, siendo Ethereum [AW19] una de las más conocidas. Este lenguaje,
influido por otros lenguajes como C++, Python y JavaScript, fue propuesto por Gavin
Wood a mediados de 2014 y desarrollado más tarde por un equipo de Ethereum. Tiene un
tipado estático, es decir, la comprobación de tipos se realiza en tiempo de compilación en
vez de en tiempo de ejecución, admite herencia, bibliotecas, variables de estado, definición
de funciones, modificadores de funciones, eventos, estructuras de datos y tipos enumera-
dos. El uso más cotidiano que se le da a los contratos programados con este lenguaje son
votaciones, subastas o crowdfunding [Sol21b].
    Su estructura básica es la que se muestra en la figura 2.1, donde la primera línea indica
a partir de qué versión de Solidity se puede ejecutar ese contrato, luego se especifica el
nombre del contrato y dentro de él se declaran todas las variables de estado, funciones,
modificadores de función (usados para poder modificar el comportamiento de las funciones
de una manera ágil, pudiendo comprobar automáticamente una condición antes de ejecutar
una función), eventos, estructuras y enumerados.

2.3.     Scilla

   Scilla [Sci19] es otro lenguaje de programación para contratos inteligentes, desarrollado
para crear estos contratos en una cadena de bloques llamada Zilliqa. Este lenguaje está
10                                                               Capítulo 2. Preliminares

      pragma solidity >= 0.4
      contract SimpleStorage{
        uint storeData;
        function set(uint x) public {
          storeData = x;
        }

          function get() public view returns (uint){
            return storeData;
          }
      }

               Figura 2.1: Ejemplo de un contrato escrito en Solidity ([Sol20])

caracterizado por su sencillez y por su sintáxis reducida, lo que facilita la programación
para colectivos que no tienen mucha experiencia en programación e impone una estructura
determinada a los contratos inteligentes, lo que hace que sean menos vulnerables a ataques.
Sin embargo, no es un lenguaje Turing-completo, por lo que cierta funcionalidad disponible
en otros lenguajes no estará disponible en Scilla.
    Su estructura básica se presenta en la figura 2.2. Observamos que en primer lugar es
necesario indicar la versión de Scilla. Aunque en este ejemplo no hay ninguna biblioteca
estándar importada, en el caso de requerirlo se puede hacer con import NombreBiblioteca
y estas bibliotecas son BoolUtils, IntUtils, ListUtils, NatUtils y PairUtils, donde se definen
operaciones específicas de cada tipo. A continuación, se pueden definir bibliotecas nuevas
con la palabra reservada library, como se ve en la figura, y seguidamente iría el cuerpo
de esa biblioteca con expresiones let, que se explicarán más adelante.
    A continuación se codifica el contrato propiamente dicho, indicando su nombre, los
parámetros inmutables y las restricciones, en el caso de que las haya. Estas restricciones
son requisitos que se imponen a los parámetros inmutables, para evitar que se despliegue
el contrato con valores que no tienen sentido.
    Después se indican los campos modificables, definidos con field. Estos campos se
inicializan con un valor y luego pueden ser modificados en las transiciones o procedimientos.
Estas transiciones y procedimientos sirven para definir funciones. La sintaxis de ambos es
muy parecida, su diferencia radica en que las transiciones son públicas y pueden llamarse
desde el exterior del contrato y los procedimientos, por el contrario, son privados y solo
pueden llamarse desde otro procedimiento o transición.
   Es importante indicar que hay algunos parámetros que están implícitos. Por una parte
tenemos los siguientes parámetros inmutables:

       _this_address: indica la dirección del contrato y es de tipo ByStr20 (este tipo será
       explicado más adelante).

       _creation_block: indica el número de bloque donde se ha desplegado el contrato y
       es de tipo BNum.

     Por otro lado, también hay otros parámetros implícitos en las transiciones que son:
2.3. Scilla                                                                              11

              Figura 2.2: Estructura básica de un contrato de Scilla ([Sci19])

      _sender: expresa la dirección de la cuenta que realizó la transición y es de tipo
      ByStr20.
      _amount: expresa la cantidad entrante y es de tipo Uint128.

    Estos parámetros se pasan directamente a los procedimientos que son llamados por las
transiciones.
   Una vez aclarada la estructura de un contrato de Scilla, se pasa a ver los tipos de datos
que se pueden tener y sus operaciones, empezando por los primitivos, que son los tipos más
básicos, y continuando con los algebraicos, que son tipos compuestos definidos mediante
constructoras. Dentro de los tipos primitivos están:

      Enteros: engloban los enteros con signo y sin signo. Pueden ser de 32, 64, 128 o 256
      bits. Un ejemplo sería:
      let a = Uint32 1
12                                                                Capítulo 2. Preliminares

       Para declarar el entero sin signo de 32 bits con valor 1. Las operaciones que se
       puede realizar con ellos son: eq (igualdad), add (suma), sub (resta), div (división),
       rem (resto), lt (menor), pow (potencia), isqrt (raíz cuadrada), to_nat (convertir
       el entero al tipo nat que explicaremos más adelante) y to_(u)int32/64/128/256
       (para transformarlo a otro tipo de integer). Estas operaciones se declaran usando la
       expresión builtin.
       Un ejemplo para declarar la operación suma sería builtin add x1 x2, donde x1 y
       x2 están declarados como enteros del mismo tipo.
       Aclarar que todos los parámetros de las operaciones tienen que ser del mismo tipo,
       excepto en la operación pow, que el segundo argumento siempre tiene que ser del tipo
       Uint32 y el primero de cualquiera. Otra excepción es la operación isqrt, que solo
       admite el tipo de entero sin signo. Este tipo además tiene una biblioteca predefinida
       (IntUtils) que aporta más operaciones, sobre todo de comparación entre dos enteros.

       String: este tipo corresponde a las cadenas de caracteres delimitadas entre comillas.
       Sus operaciones son: eq (igualdad), concat (concatenar dos strings), substr (obtener
       parte del string), to_string (convertir un tipo entero o hash a un string) y strlen
       (obtener la longitud del string). La forma de declararlas es igual que para los enteros.

       Cadenas de bytes (ByStr y ByStr x, donde x es la longitud): se escribe utilizan-
       do caracteres hexadecimales, precedidos por “0x”. Admite las siguientes operaciones
       declaradas también con builtin: to_uint (convertirlo a un valor equivalente del
       entero), concat, strlen y eq. Este tipo es el general, además encontramos las direc-
       ciones, que corresponden a un tipo específico (ByStr20, cadenas de 20 bytes).

       Mapas: son listas de parejas clave-valor y se declaran con Map kt vt (donde kt es
       el tipo de la clave y vt el tipo del valor) y para declarar el mapa vacío sería Emp kt
       vt. Las operaciones que admite pueden ser de dos tipos:

          • In_place: modifica el mapa sin realizar copias de él. Estas operaciones pueden
            ser múltiples, es decir, referirse a varias claves a la vez).
          • Funcionales: no modifica el mapa original y normalmente se usan para el diseño
            de bibliotecas.

       Se implementan las mismas operaciones en ambos casos, la única diferencia es que se
       escriben de forma distinta. Permiten insertar un par clave-valor, si no está la clave
       ya en el mapa, o modificar el valor si ya está; obtener el valor de una clave; saber si
       una clave tiene un valor asociado; eliminar el par clave-valor; convertir el mapa en
       una lista; y devolver el número de pares que hay (estas dos últimas operaciones no
       distinguen entre operación funcional o in_place).

       Número de bloque: se declara con la palabra BNum y un entero. Las operaciones que
       admite este tipo son: eq (igualdad), blt (menor), badd (suma) y bsub (resta).

     En cuanto a los tipos algebraicos, se tiene:

       Booleanos (Bool): tiene dos constructores, True y False. Para este tipo hay una
       biblioteca estándar (BoolUtils) que aporta operaciones lógicas sobre ellos.
2.3. Scilla                                                                               13

      Opciones (Option): tiene los constructores Some, que indica la presencia de valor
      y recibe un argumento (el valor), y None que es la ausencia de valor y no tiene
      argumentos. Este tipo es útil para definir funciones parciales.

      Listas (List) tiene también dos constructores, Nil corresponde a la lista vacía y
      Cons construye la lista no vacía recibiendo dos argumentos, el primero corresponde
      a la cabeza de la lista y el segundo al resto. Este tipo también tiene una biblioteca
      estándar (ListUtils) que aporta operaciones para interactuar con ellas, como por
      ejemplo, obtener la cabecera, filtrar en la lista, etc.

      Pares (Pair) tiene un solo constructor Pair con dos argumentos. En este caso, tam-
      bién hay una biblioteca ya definida (PairUtils) con dos operaciones, fst, que extrae
      el primer elemento del par y snd, que extrae el segundo.

      Naturales: tiene dos constructoras, Zero que representa el 0 y no tiene argumentos
      y Succ con un argumento y representa el sucesor de un número. También tiene una
      biblioteca estándar ya definida (NatUtils).

    También el usuario puede definir tipos propios con la siguiente sintaxis:

     type  =
         |
         |
         ...

    A continuación se especifican las expresiones y declaraciones para usar estos tipos tanto
en las bibliotecas definidas por el usuario como en los procedimientos y transiciones.
    Las expresiones a continuación se usan principalmente en las bibliotecas, aunque tam-
bién pueden usarse dentro de las declaraciones de transiciones y procedimientos:

      let x = f se usa para declarar una variable (x) en las bibliotecas y f puede ser
      cualquier tipo de los comentados anteriormente.

      let x = f in expr declara la variable x e indica que solo se puede usar dentro del
      ámbito de expr.

      {; ; ...}: usado para representar mensajes o eventos, don-
      de cada entrada tiene la forma b : x.
      Decir que estos eventos y mensajes tienen entradas obligatorias. Para los eventos, solo
      hay una y es _eventname (String), que indica el nombre del evento. Los mensajes
      tienen tres entradas obligatorias: _tag (String), _recipient (BStr20) y _amount
      (Uint128), que indican el nombre de la transación que se invocará en el contrato
      destinatario si _recipient es la dirección del contrato, si no se ignora; la dirección
      de la cadena de bloques a la que se envía el contrato; y la cantidad que se transfiere,
      respectivamente.

      fun(x:T) =>expr es una función cuya entrada es x de tipo T y devuelve un valor.

      f x aplica la función f al parámetro x.
14                                                               Capítulo 2. Preliminares

       tfun ’T =>expr: función que toma a ’T como un tipo paramétrico y devuelve un
       valor. Sirve para crear funciones de biblioteca y es útil cuando no se sabe qué tipo
       va a tener el valor devuelto.

       @x T indica que el parámetro de entrada de la función x va a tomar el tipo T. Esto
       se usa cuando se tiene una tfun.

       builtin f lx (mencionado al explicar las operaciones de los tipos): aplicar la ope-
       ración f con los parámetros lx.

       match: tiene la misma función que un if y su sintaxis es:

           match x with
               | Opcion1 => ...
               | Opcion2 => ...
               ...
           end

       Donde opcion1, opcion2, ... son los posibles valores que puede tomar x.

   A continuación, se explican las declaraciones, que se usan solo dentro de transiciones y
procedimientos:

       x
2.4. Trabajo relacionado                                                                      15

de programación Java y C. También se hará mención a la creación de la KEVM por este
mismo autor y, posteriormente, se analizarán de forma muy breve dos estudios sobre el
límite de gas y los problemas que tienen los callbacks 1 así como la solución propuesta.
    En [MR06] se define una hoja de ruta para la definición de semánticas en lógica de
reescritura y, en particular, en Maude. En primer lugar, se hace notar que a la hora de
especificar un lenguaje de programación hay muchos estilos diferentes para hacerlo según lo
que se quiera analizar, por lo que para un mismo lenguaje puede haber varias definiciones
en reescritura lógica.
    Además, se menciona que se han realizado especificaciones de grandes fragmentos de
lenguajes complejos, como Java, usando lógica de reescritura en Maude. Un ejemplo de
esto es JavaFAN [FMR04], una herramienta de análisis de software para Java con un muy
buen rendimiento, realizando la especificación en Maude de la semántica de la JVM (la
maquina virtual de Java o Java Virtual Machine). Esto nos permite analizar programas
concretos que en algunos casos proporcionan mejores resultados que algunas herramientas
conocidas de análisis de Java.
    Lo que se ha visto hasta el momento tiene un problema y es la falta de escalabilidad
de las semánticas. Una de las causas es la falta de modularidad, es decir, la falta de ver la
semántica como la unión de varias partes. Por lo tanto, se buscó un método que resolviese
este problema, dando lugar al llamado K-framework [MR13].
    El K-framework es un entorno específico para la definición de semánticas de lenguajes y
se encarga de definir funcionalidad para simplificar el proceso. En sus inicios, este entorno
estaba basado en Maude pero sus últimas versiones se han implementado en Java. El
éxito de este entorno se fundamenta en tres elementos: configuraciones, cálculos y reglas.
Las configuraciones organizan el programa en una estructura de datos llamada celda, que
están etiquetadas y se pueden anidar. Por otro lado, los cálculos lo que hacen es ampliar el
programa y, por último, las reglas generalizan las reglas de reescritura normales indicando
qué partes del término leen, escriben o les son indiferentes. Se puede ver un ejemplo de esta
técnica sobre el lenguaje de programación IMP explicado al detalle en el artículo [MR13],
concretamente en la sección 3.1.
   Este método se puede ejecutar sobre Maude usando la herramienta K-Maude, que
permite transformar las definiciones del lenguaje K en Maude para poder ejecutarlo y
analizarlo. También es posible exportarlo a Latex para generar la documentación [SR10].
    A continuación, se pasará a hablar de los trabajos relacionados con la reescritura del
lenguaje C [MR13] usando el entorno K-framework, destacando que es la semántica más
compleja que se ha realizado de este lenguaje hasta el día de hoy. Es especialmente intere-
sante notar que esta semántica se ha ejecutado con éxito en la mayoría de los ejemplos que
hay en el manual de Kernigham y Ritchie2 . Si se observa la tabla 2.1 se puede apreciar que
la semántica creada con K-framework es una muy buena alternativa al compararla con los
compiladores GCC, ICC y Clang, ya que da resultados muy similares y en algunos casos
mejores. Otra carácterística a favor es el tiempo que tarda en ejecutarlos, ya que la mayoría
(más del 90 % de los casos de éxito) se ejecutan en menos de 5 segundos cada uno de ellos.
   Tras ver lo que es el método K-framework y su uso en lenguajes de programación
complejos como es el caso de C, se pasa a explicar en que consiste KEVM [HSR+ 18].

  1
      Un callback es un valor devuelto por una función y que se usa como argumento en otra.
  2
      Manual que cubre todas las carácterísticas de ANSI C, un estándar para el lenguaje C.
16                                                                            Capítulo 2. Preliminares

                          Compilador/Semánticas          Pruebas éxito      Porcentaje
                              K-framework                    770               99.2
                                  GCC                        768                99
                                   ICC                       771               99.4
                                  Clang                      763               98.3

          Tabla 2.1: Comparación entre K-framework y compiladores conocidos ([MR13])

                  Figura 2.3: Fallos en distintos contratos y sus pérdidas [HSR+ 18].

Esta herramienta es un entorno de especificación de la EVM de Ethereum3 usando el
K-framework.
    Esta semántica para la EVM tiene tres elementos principales. El primero de ellos es una
sintaxis del lenguaje en estilo EBNF4 , el segundo es una configuración propia para describir
el estado y, por último, las reglas de transición para ejecutar los programas. Estos elementos
van a ser fundamentales para especificar cualquier semántica de contratos inteligentes. En
comparación, en Maude tenemos ecuaciones para definir la sintaxis del lenguaje; reglas,
que ejecutarán el contrato; y una configuración de los estados que se van a tener y cuyas
transiciones especificarán las reglas.
    La necesidad de desarrollar esta nueva herramienta surge de la escasa seguridad y de
los graves errores que han tenido ciertos contratos inteligentes causando pérdidas bastante
cuantiosas a las partes involucradas (en la imágen 2.3 se representas los distintos contratos
inteligentes que han sufrido algún fallo y sus respectivas pérdidas [HSR+ 18]). Esto se
complica aún más ya que la EVM admite que un contrato llame a otro para así reutilizar
código a través de las bibliotecas. La mayoría de estos fallos se podrían haber evitado si
esos contratos se hubieran analizado formalmente y esto es lo que va a aportar KEVM.
    En definitiva, KEVM es una herramienta muy potente pero no realiza la especificación
de Scilla. Además, Maude es un lenguaje que proporciona muchos tipos análisis, por lo
que una especificación en Maude es potencialmente analizable con una amplia variedad de
técnicas.
    A continuación comentamos distintos estudios sobre el gas en los contratos inteligen-
tes, para así enfatizar la importancia de los métodos formales en este tipo de análisis.
En [ACG+ 21] se presenta un análisis cuyo objetivo es inferir los límites de gas que no sean
constantes en los contratos inteligentes. Para ello usan una herramienta llamada GASTAP
que analiza contratos inteligentes teniendo en cuanta el gas. GASTAP funciona en base a
un contrato inteligente que toma como entrada, aportando unas cotas superiores de gas
para aquellas funciones o transiciones que sean públicas. Usando esta herramienta se con-
siguieron unos buenos resultados, ya que se encontraron cotas para aproximadamente el
     3
         EVM es la máquina virtual de Ethereum, es decir, el intérprete de los contratos inteligentes
     4
         Es una notación formal para definir la sintaxis de lenguajes.
2.4. Trabajo relacionado                                                                  17

90 % de los contratos que se analizaron.
    Por último, en [AGR+ 20] se analizan los callbaks en los contratos inteligentes. Estos
callbaks pueden desencadenar algunos errores sobre todo en entornos abiertos. De hecho,
muchos atacantes lo aprovechan para llevar a cabo su objetivo. Esto es lo que se quiere
evitar buscando la modularidad, es decir, lo que se pretende es asegurar que las llamadas
que se producen desde exterior no pueden afectar al comportamineto interno del contrato.
Este estudio descibe una técnica para solucionarlo usando resolutores SMT (Satisfiability
Modulo Theories), que permiten generar contraejemplos que ayudan a comprender y así
solucionar el error del callback. Esta análisis ha sido implementado y aplicado con éxito
a contratos reales, aproximadamente a los 150 contratos más importantes de Ethereum.
En esencia, los autores decompilan los programas de código de bytes (el formato que
genera la EVM) para tener una representación intermedia, para luego sobre ella realizar la
verificación de la modularidad mediante SMT.
    En definitiva, uno de los objetivos de tener una especificación formal de Scilla en Maude
es poder ser capaz de hacer análisis similares a ellos en el futuro.
Capı́tulo    3
Descripción del Trabajo

    En esta sección se hablará de cómo se ha desarrollado este trabajo. En particular,
presentaremos una herramienta que permite analizar sintácticamente contratos inteligentes,
ejecutarlos y realizar sobre ellos un análisis para detectar partes de código que no se usan.
Este análisis está parametrizado por distintos elementos de la gramática, por lo que sería
reutilizable si en el futuro se extendiese la herramienta con nuevos lenguajes de contratos
inteligentes.
    Todo el código se encuentra en el siguiente GitHub (https://github.com/laudf/
Especificacion-y-verificacion-de-Scilla-en-Maude) y es interesante conocer los cua-
tro ficheros principales en los que se distribuye:

Preparse: este archivo solo contiene un módulo funcional con una única operación llamada
    preparse que se encarga de preparar el archivo que se recibe como entrada para poder
    parsearlo posteriormente. Por ejemplo, en Maude los guiones bajos (_) los detecta
    como posiciones para colocar los argumentos, por lo que no se puede definir ningún
    operador constante con este símbolo. Gracias al preparse se puede modificar todas
    los “_” por “-”. Este aspecto se explicará en más profundidad en la sección 3.2.

Gramatica_def: en este archivo se tienen dos gramáticas, ambas en módulos funcionales.
    En la primera de ellas está la gramática que se usa para realizar el análisis sintáctico
    usando la función metaParse y que tiene categorías sintácticas que no están com-
    pletamente definidas, como son los tokens, que necesitan un análisis posterior para
    ser resueltos. Por otro lado, hay otra gramática que utiliza tipos propios de Mau-
    de para representar aquellos términos que quedaron “abiertos” en la fase anterior.
    Aunque esta representación es ya distinta a la usada por Scilla, será la que se use
    internamente para la ejecución. Asimismo, se define un módulo funcional, donde se
    realiza el parse, es decir, la modificación de la gramática de Scilla a la representación
    interna deseada para trabajar con ella. Y por último, se define otro módulo funcional
    para definir la memoria, usada para guardar las variables, declaraciones, expresiones,
    transiciones y procedimientos. Por último, contiene otros módulos de sistema donde
    se define la semántica, es decir, estos módulos contienen las reglas para acceder a
    memoria y para realizar la ejecución propiamente dicha.

EntradaSalida: este archivo, que importa los anteriores, contiene toda la interacción del
    usuario con el sistema. Está formado por un único módulo de sistema que contiene

                                                                                          19
20                                                   Capítulo 3. Descripción del Trabajo

       todas las reglas necesarias para la entrada/salida.

CodigoMuerto: dentro de este archivo se realiza el análisis para detectar código muerto
    dentro de una función, procedimiento o transición. Es decir, este código muerto son
    partes que están declaradas pero que nunca se usan. En la sección 4 se verán ejemplos
    de ello.

    La figura 3.1 muestra un diagrama de flujo ilustrando la ejecución que se realiza inter-
namente. una vez que se tiene el fichero cargado y leido, se pasa al preparse, metaparse
y parse, luego se hace una distinción en función de lo que decida el usuario, si pulsa 1,
se analizará el código y se acabará la ejecución del sistema o si, por el contrario, se pulsa
cualquier otro valor distinto de 1, se guardan los datos en la memoria, se solicitan los pa-
rámetros necesarios y una transición a ejecutar, una vez que se tienen todos los datos, se
ejecuta la transición, se devuelven unos valores y se vuelve a pedir otra vez una transición.
La parte de pedir transición, sus parámetros de entrada y ejecutarlo se realiza de forma
reiterada hasta que el usuario decida salir.

           Figura 3.1: Representación del flujo que sigue el sistema internamente

   Una vez vistos los archivos que tiene el trabajo y el flujo que sigue, se va a explicar
cada una de las partes de forma detallada en base a un ejemplo sencillo (figura 3.2), donde
simplemente se obtiene el valor de un campo modificable.

3.1.     Entrada/Salida

    La entrada/salida, como ya se ha mencionado antes, sirve para que el usuario pueda in-
teractuar con el sistema. Está programado dentro del archivo llamado EntradaSalida.maude,
en el módulo de sistema SciMaude.
   Anteriormente, en la sección 2.1, se ha comentado cómo era la estructura básica de un
programa de entrada/salida leyendo de archivo, y ahora se verá las reglas nuevas que se
3.1. Entrada/Salida                                                                      21

    scilla_version 0
    contract HelloWorld
    ()
    field welcome_msg : String = "Hola"
    transition getHello ()
        r
22                                                   Capítulo 3. Descripción del Trabajo

      suprimir indica que se quieren suprimir campos inutilizados en la memoria.

      cogerTransición indica que el estado debe pedir el nombre de la transición y coger
      la parte de la memoria que corresponda.

      pedirParamTransicion pide los parámetros de entrada de la transición elegida.

      cargarParamTransicion carga los parámetros de entrada de la transición con los
      valores introducidos por el usuario.

      eleccion sirve para preguntar si se quiere ejecutar el código o, por el contrario, si
      se quiere analizar.

      eleccionHecha comprueba si la elección del usuario ha sido ejecutar el código o
      analizarlo.

      analisis indica que se va a analizar si el contrato introducido tiene código muerto.
      terminar indica que el programa ha finalizado tras analizarlo.

             Figura 3.3: Representación del flujo que sigue la Entrada/Salida

    En la figura 3.3 se muestra una representación del flujo que siguen las reglas de la parte
de entrada/salida. Estas reglas tienen la siguiente funcionalidad:

      La regla parsing se encarga de realizar la conversión de String a Qid, usando la
      función predefinida tokenize. Si aplicamos esta transformación al ejemplo en la
      figura 3.2 se obtiene el resultado en la figura 3.4, donde simplemente se ha convertido
      cada “palabra” en un Qid. Sobre está lista aplicaremos las fases de preparse, metaparse
      y parse, como explicaremos en las siguientes secciones.
También puede leer