Skip to content

Instantly share code, notes, and snippets.

@glihm
Created April 12, 2023 06:36
Show Gist options
  • Save glihm/ed2629e44ef5f68dc094d90d60fdc5bc to your computer and use it in GitHub Desktop.
Save glihm/ed2629e44ef5f68dc094d90d60fdc5bc to your computer and use it in GitHub Desktop.
Docker para desarrollo de Cairo

Docker para el desarrollo de Cairo

Cairo es el lenguaje utilizado en Starknet para escribir contratos. Últimamente, la versión de Cairo pasó de Cairo 0 a Cairo 1, lo cuál implica un cambio de sintaxis pero también las herramientas de desarrollo.

Además, la implementación de referencia esta migrando de python a rust. Cairo lang serà en los próximos meses reemplazado por starknet-rs implementado en starkli. Pero hasta que esto sea hecho, necesitamos poder compilar Cairo 1, y utilisar el cli de cairo-lang para desplegar el código compilado on-chain.

Para evitar que configurar el entorno de desarrollo, aquí viene una guía de una primera exploración usando Docker para esto.

Se considera que docker ya está instalado en sus sitema.

Construir la imagen

Para construir la imagen de docker con el entorno, se usará como imagen de base la imagen de starknet mantenida por el desarrollador de starknet-rs.

Luego, se requiere instalar cairo-lang, el cuál no tiene imagen disponible. Entonces repetimos los pasos de instalación que se pueden encontrar en el tutorial de starknet-edu.

# Dockerfile

FROM starknet/cairo:1.0.0-alpha.6 AS build

FROM python:3.9-alpine

# Aquí se pueden agregar varios programas que se ocuparian para trabajar más cómodos.
RUN apk add --update gmp-dev build-base nodejs npm git zsh curl
RUN sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
RUN python -m pip install --upgrade pip

RUN pip3 install ecdsa fastecdsa sympy
RUN pip3 install cairo-lang

ENV STARKNET_NETWORK=alpha-goerli
ENV STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount

COPY --from=build /usr/bin/* /usr/bin/
COPY --from=build /corelib /corelib

ENTRYPOINT [ "sh" ]

Para hacer el build, se usa el comando: docker build -t starknetes-cairo-goerli .. (Se usa . al final, considerando que se ejecuta el comando estando en el mismo directorio que el Dockerfile).

Nota importante:

En un Dockerfile se puede usar varias veces el FROM. Esto implica un build en varios etapas. De que sirve? Basicamente el build en varias etapas permite utilizar docker para configurar un entorno, hacer en build de un binario específico, y luego solo copiar el binario. Así, todo el entorno que se uso para compilar no se transmite a la imagen final, ayudando a conservar una imagen final más pequeña, con solo el binario que nos interesa.

Entonces aquí, usamos la imagen de starknet/cairo como base, pero la etiquetamos como build, porque lo que no interesa es que la imagen haga el build de todo el compilador cairo, y recuperamos los binarios y la libraria corelib, que se requiere al momento de compilar el contrato.

Pero cuidado, usando dos veces FROM, si no se etiqueta el primero, el secundo tomará precedencia y no se veran reflejados los cambios del primero. Por este razón aquí, usamos primero el FROM starknet/cairo:1.0.0-alpha.6 AS build, para poder luego hacer referencia a este etapa COPY --from=build .....

Correr el contenedor

En docker, se puede correr un contenedor a partir de una imagen con docker run. Sin embargo, cada vez que lanzamos el comando, se genera un nuevo contenedor. Y como recordatorio, cada vez que se genera un contenedor, toda su memoria esta nueva, nada es persistente si no está en un volumen montado al momento del run. De igual forma, un contenedor una vez generado, se puede "apagar" y "prender" sin problema, los datos no se quitan. Pero si se elimina el contenedor, ahí si todo se borra (excepto los volumenes, porque son persistentes).

Para montar un volumen, solo se tiene que pasar la opción -v /path/en/mi/compu:/path/en/el/contenedor. Así, todo en contenido del path en el host (la computadora que usamos) se verá reflejado en el path que indicamos en el contenedor. (Se vale mencionar que si indican un path en el contenedor que no exista, docker lo creará solito)

Así mismo, se pueden considerar dos formas de usar la imagen:

  1. Comó herramienta, lo que significa que se instancia un contenedor para un solo comando, se ejecuta, y luego se eliminar el contenedor. Al final solo lo usamos por su capacidad a correro comandos.

  2. Comó una máquina virtual, en el cuál uno se conecta para trabajar, y se desconecta cuando termina.

Los ejemplos se basan en el tutorial de starknet-edu, para replicar un ejemplo funcional en la testnet goerli. Se usarán directorios para el demo que se pueden adaptar sin problema a su caso de uso.

Por ejemplo, si leen ~/cairo-files en los comandos, es simplemente una ruta hacia un directorio en local donde se copian los archivos para que el docker les pueda tener. Puede ser ~/mi-proyecto-contratos, o lo que quieran. Guardan en mente que usando la opción -v, damos a docker la posibilidad de acceder al directorio (tanto para leer que escribir), y este directorio es persistente en nuestra computadora. Así que aunque se apaga docker o se elimina el contenedor, se conservaran los datos.

Importante, en este versión del Dockerfile, se usa el testnet, y no se puede cambiar si no se reconstruye la imagen. Se puede imaginar un nuevo Dockerfile donde STARKNET_NETWORK se puede pasar comó variable a cada llamada de docker run con la opción -e STARKNET_NETWORK=alpha-goerli o usando un archivo .env.

La ventaja, es que la misma imagen podría servir para tanto goerli y mainnet.

Preparar los datos

Antes de empezar, se tiene que preparar los datos. Solo se ocupa un archivo: un contrato en cairo 1.

Copiar el contrato de demo en un archivo. Cuidado, es importante cambiar líneas de código en este contrato para poder desplegarlo on-chain. La razón es que en Cairo, el hash del contrato depende de su contenido, solamente cambiar el nombre de las funciones no es suficiente!

Para cambiar el código, solo agregan unas instrucciones sencillas de Cairo, o pueden agregan eventos por ejemplo, aunque no los usan.

En el caso del tutorial, este es la ruta del contrato en la computadora host: ~/cairo/contrato.cairo.

Uso comó herramienta

Si solo se quiere usar el contenedor como herramienta, y no como una máquina virtual en donde se trabaja de manera constante, así sería el flujo:

  1. Compilar el contrato: docker run --rm -v ~/cairo:/work --entrypoint starknet-compile starknetes-cairo-goerli /work/contrato.cairo /work/contrato.json --replace-ids

    Unas notas sobre este comando:

    • --rm indica que cuando se ejecuto el comando, se elimina el contenedor. Por esto es importante que los datos persistentes siempre se encuentran en el volumen. En este caso, /work dentro del contenedor.
    • -v para montar el volumen, y tener nuestros datos persistentes accesibles en el contenedor.
    • --entrypoint permite nombrar un comando que queremos ejectuar en el contenedor. Esto es posible porque al fin del Dockerfile, el entrypoint es sh, un shell donde ya estan exportados los binarios en /usr/bin/.
    • despues pasamos el nombre de la imagen a partir de la cuál queremos el contenedor.
    • Todo lo que esta despues el nombre de la imagen, se pasa como argumentos adicional al momento de ejecutar el entrypoint.
    • Si se fijan, todo esta en /work, que es el volumen que montamos al momento de correr el contenedor. Así, todos los archivos que están en /work son accesible para docker y también para la computadora host.
  2. docker run --rm -v ~/cairo:/work --entrypoint starknet starknetes-cairo-goerli new_account --account starknetes_1 --account_dir /work

    Con este comando, que pasa:

    • Usamos aquí la opción account_dir para poder indicar donde queremos que se genere el archivo json con la cuenta creada. Esto a fines de persistencia de la cuenta, incluso si se elimina el contenedor. Se tendrá entonces que repetir en todos los otros comandos de starknet para que la cuenta se pueda utilizar. Es necesario usar este opción en el modo herramienta porque cada comando que ejecutamos se ejecutar en un contenedor diferente!.
  3. docker run --rm -v ~/cairo:/work --entrypoint starknet starknetes-cairo-goerli deploy_account --account starknetes_1 --account_dir /work

etc...

Y así, se puede seguir el docker como herramienta, de mandera muy "independiente". No estamos registrando ninguno dato dentro del contenedor. Todo esta en el volumen.

(Pueden seguir con los ejemplo de starknet-edu, como ya esta en el docker todas las herramientas, pueden sin problema ejecutarlas.

PROS: completo aislamiento entre persistencia de datos, y ejecución.
CONS: un poco pesado de escribir los comandos.

Uso como máquina virtual

Ahí el punto es usar el contenedor como una pequeña máquina virtual. SIN EMBARGO, sigue importante siempre respaldar la información en el volumen. Recordamos que un contenedor se puede "apagar" y "prender", y los datos no se pierden. Pero si por alguna razón se elimina el contenedor, todos los datos que no están en un volumen se pierden.

  1. Levantar el contenedor con el comando: docker run --detach -it --name starknetes_c1 -v ~/cairo:/work starknetes-cairo-goerli Aquí, unas cosas que notar:

    • --detach permite levantar el contenedor en background.
    • -it permite conservar STDIN abierto y tener un tty cuando nos queremos conectar al docker. Agregando este opción es suficiente para tener el contenedor corriendo para siempre.
    • --name para poder facilmente referenciar el contenedor.
  2. Para conectarse al contenedor corriendo: sudo docker exec -it starknetes_c1 /bin/sh. Aquí, usando el --name de la etapa precedente, nos podemos conectar al docker. El argumento al final /bin/sh es el comando que queremos ejecutar. Y como pasamos /bin/sh, directamente se nos abré el terminal.

Y pues, de ahí, se puede utilizar el docker como una máquina virtual, utilizando los comandos de starknet ya instalado. Pero ojo... siempre conservar en la mente que se pueden dejar datos en el contenedor (y no en el volumen) mientras no se elimina el contenedor con el comando docker rm.

Pero se pueden usar sin problema los comandos docker start or docker stop.

PROS: muy familiar con una máquina virtual, comandos más faciles a entrar sin docker run etc..
CONS: riesgo de no poner datos en los volúmenes, que podría resultar en perdida total de los datos con un descuido (lo más de riesgo es la cuenta con la clave privada.. si se pierde, pues todos los goerli ETH se pierden...)

Ambos métodos son muy viables, esto depende de los gustos/costumbres de cada quién.

@ccolorado
Copy link

Hey I attempted to manage my full cairo devenv with this docker image and faced a rather confusing error when trying to use use scarb

The shell failed to run scarb saying it couldn't fin it despite being correctly added to the path (and type scarb correctly identified the binaries)
Long story short installing libc6-compat and gcompat fixes this

Long story ... long ? here is a more in-depth explanation
https://stackoverflow.com/questions/66963068/docker-alpine-executable-binary-not-found-even-if-in-path

Hope this info is useful for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment