¡Buenas tardes a todos! He estado trabajando en desarrollo backend durante bastante tiempo y, durante los últimos años, he estado escribiendo cada vez más proyectos blockchain diferentes (Solidity en EVM). Sumergirme en blockchain no fue fácil para mí y mi cerebro backend falló varias veces, por lo que decidí compartir mi punto de vista sobre el cambio al desarrollo blockchain.
Descargo de responsabilidad: todo lo que se describe a continuación es puramente mi opinión. Podría estar equivocado, y me equivoco con frecuencia:))
La cadena de bloques es una tecnología muy interesante que puede hacer avanzar nuestro mundo. Pero, por ahora, mucha gente la usa para comprar, vender, estafar, explotar. No consideraré ni tengo intención de considerar las criptomonedas como un activo.
Sí, hay muchos videos y publicaciones que dicen: “Ahora esta criptomoneda puede crecer y esta criptomoneda ha caído. Chicos, invirtamos, compremos…”. No les diré nada al respecto.
Solo diré una cosa: debes evitar las criptomonedas para ganar dinero. Si eres desarrollador, es mejor que intentes ganar dinero como desarrollador y no te involucres en inversiones. Y si realmente quieres invertir, no te metas en criptomonedas.
Vale la pena comenzar con una nota importante. Blockchain no es una criptomoneda. Blockchain es la tecnología sobre la que se construye la criptomoneda y llamar a una criptomoneda una blockchain es como llamar a toda la industria del desarrollo Javascript . Sí, JavaScript parece ser un caso especial de desarrollo, pero cuando hablamos de desarrollo, no nos referimos a JavaScript. Aunque algunas personas solo se refieren a JavaScript...
Lo primero que me viene a la cabeza es el dinero. Los desarrolladores de blockchain están muy bien pagados. Yo mismo he abierto puestos vacantes de este tipo. He respondido personalmente a puestos de este tipo, en los que se puede conseguir más que el mismo desarrollador backend por la misma cantidad de tiempo dedicado al día en el trabajo. Los desarrolladores de blockchain valen su peso en oro en las empresas emergentes basadas en blockchain. ¡Especialmente un buen desarrollador de blockchain!
Convertirse en un buen desarrollador backend es imposible sin abandonar o reparar un producto. Tal vez sea un perdedor y solo aprenda a través de las experiencias negativas, pero esa es la teoría que tengo:
No se puede intercambiar la experiencia por el fracaso, pero la experiencia del fracaso te permite darte cuenta de que tienes experiencia. Es difícil convertirse en un buen desarrollador de blockchain sin perder dinero:
La imagen de arriba muestra el primer avión de los hermanos Wright en el mundo, que volaba bastante mal. Pero volaba, y en su época, la actitud de la persona promedio ante los aviones era más o menos así:
Y ahora, la industria aérea es una parte maravillosa de nuestras vidas, conectando a personas de todo el planeta en cuestión de horas. ¡La logística ha alcanzado un nivel con el que los hermanos Wright nunca soñaron! El mundo entero vive de manera diferente gracias a los aviones.
Diría lo mismo de la tecnología blockchain actual: es cara e incómoda y no está claro por qué. Hasta que me sumergí en el desarrollo de la tecnología blockchain, me parecía algo inútil para engañar a los inversores (= hámsteres). Pero si lo miras desde el otro lado, es posible almacenar cualquier dato de forma descentralizada sin posibilidad de manipulación. El "sin posibilidad de manipulación" es un detalle importante.
Pero, por desgracia, las asociaciones con la palabra “blockchain” son más bien aburridas y monótonas:
Si arrojamos un fajo de billetes a la estufa, la estufa arderá bien y el dinero incluso dará calor durante algún tiempo, pero eso es absurdo.
Y lo mismo ocurre con la cadena de bloques. Usarla solo para dinero y criptomonedas es malo, pero lo otro aún no está arraigando...
Uno de los principales impulsores del desarrollo de cualquier producto es el dinero. Si hay algo con lo que se pueda ganar dinero de forma rentable, entonces ese “algo” se desarrollará de forma activa. Y es por eso que, hasta ahora, los proyectos financieros basados en blockchain se basan realmente en ganar dinero para que alguien gane dinero y perder dinero para otro.
¿Cuál es la diferencia entre un servicio centralizado y uno descentralizado? Empecemos con un sistema centralizado. Estamos otra persona y yo y hemos decidido utilizar un servicio común, como un banco u otro proveedor, para facilitar una transferencia.
Imaginemos que tenemos un banco, que es un servicio centralizado. Le ordeno a este banco: “Por favor, transfiera 100 dólares a esta persona”. El banco registra que tengo 100 dólares menos y que otra persona tiene 100 dólares más.
Pero, ¿cuál es el problema con el servicio centralizado? Detrás de este servicio centralizado hay un propietario, ¿no? Normalmente, algunas grandes empresas, holdings, no importa; en nuestro caso, que sea una sola persona. Esta persona puede decir: “Hagamos esto. Que Alex envíe 100 dólares, pero nadie recibirá esos 100 dólares. Los necesito más”.
Los servicios centralizados tienen sus dueños. El problema es que el dueño puede tomar decisiones negativas, quitándoles dinero. Y no puede tratarse solo de “quedarse con el dinero para sí mismo”. Por ejemplo, se puede escribir: “Tenemos 100.500 dólares en el banco” y esperar que todos los depositantes no vayan a por ese dinero… como sucedió con SVB y otros bancos que desaparecieron.
Bitcoin fue inventado para descentralizar la gestión del dinero.
Un servicio descentralizado se basa en una red de nodos, donde cada nodo almacena y transmite información. En pocas palabras, los nodos han acordado qué información se considera correcta y cuál no, y cómo la almacenamos.
Podemos hacer una analogía con una sala: una persona grita la información que quiere que los demás conserven. Luego, todos los que están en la sala trabajan con la información que han almacenado después de gritar.
Por ejemplo, una cadena de bloques puede almacenar y transmitir mensajes o información sobre transferencias de dinero. Los participantes de la red verifican la información antes de registrarla.
En la analogía de la habitación, grito: “Estoy transfiriendo 100 dólares a Sam”. Todos registran que tengo 100 dólares menos y Sam tiene 100 dólares más. Si de repente, antes de la transferencia, tengo menos de 100 dólares, nadie registrará la transacción.
En blockchain, se pueden crear contratos inteligentes en el lenguaje Solidity (en el caso de blockchain en EVM). Un contrato inteligente es un programa que se ejecuta en la red blockchain. Puede contener mecanismos de validación, gestión de errores y otras funciones.
De nuevo, en el caso de la sala donde gritamos comandos, un contrato inteligente consiste en que yo le doy a cada participante de la sala el código del programa por adelantado: cómo reaccionar a mis comandos, qué verificar y qué guardar. Y luego grito: "Ejecuta el programa con estos parámetros". Luego, todos siguen las instrucciones. Un ejemplo de un contrato inteligente simple es el almacenamiento de información con las funciones de agregar y recibir datos. El código del contrato se compila en código de bytes y se pasa a los participantes de la cadena de bloques para su ejecución.
¿Cómo manejamos las solicitudes particulares que se enviarán a este contrato inteligente? Si hacemos una analogía con el backend, es un servicio que puede procesar solicitudes POST y GET. POST almacena información. GET aquí envía de vuelta la información que hemos almacenado. Así es como suele estar estructurado cualquier backend.
Durante mi desarrollo en el backend, me acostumbré mucho a la disposición de la API, la base de datos y todo lo relacionado con el almacenamiento y procesamiento de datos que se realiza de mi lado. Y ya, como si estuviera detrás de una pared, proporciono una interfaz para que el usuario trabaje con estos datos de acuerdo con un escenario preparado previamente.
Por ejemplo, el usuario 1 llega y guarda un contenido (una publicación, por ejemplo) mediante el método POST. Luego, el usuario 2 llega y recupera este contenido mediante el método GET. Los usuarios no saben dónde y cómo se encuentra: el backend es una caja negra para ellos.
Y aquí llegamos a una parte muy importante de la cadena de bloques. Volvamos a nuestros ejemplos de nodos o personas paradas en una habitación. Digamos que, usando una analogía con el backend, tenemos lo siguiente cada vez: lanzo un método "ADD" a la cadena de bloques, y luego todos llaman localmente al método y luego pueden tomar información de su copia de la cadena de bloques.
Por lo tanto, tenemos un montón de copias diferentes en la red de las cuales los nodos toman información. El problema con la cadena de bloques es que debemos pagar dinero real por cada operación de escritura. Esto se paga con la moneda de la red, que se puede comprar con dinero real (o minar, pero no es de eso de lo que estamos hablando hoy).
Si comparamos blockchain y backend, el panorama es el siguiente:
Por ejemplo, Telegram tiene una base de datos centralizada a la que siempre podemos acceder de forma gratuita y descargar nuestros mensajes, fotos, vídeos, etc. Pero si los servidores de Telegram de repente se caen, no podemos acceder a ella.
Tenemos que pagar para que la máquina virtual EVM ejecute algunos comandos de contrato inteligente, incluida la escritura de información en la cadena de bloques. Realiza algunos cálculos, suma algo, multiplica, multiplica, multiplica y, finalmente, aparece un nuevo artefacto en el almacenamiento de la cadena de bloques, que se actualiza en todos los nodos que participan en la cadena de bloques.
Cualquier participante de la red puede ejecutar un nodo completo con cientos de gigabytes de datos de la cadena de bloques y trabajar con ellos localmente. También puede utilizar una versión liviana del nodo, que no almacenará toda la cadena de bloques, pero puede acceder a los nodos completos de la red y recuperar la información necesaria a través de ella.
La idea es que cada entrada en la cadena de bloques es un bloque que contiene un conjunto de transacciones en las que se producen cambios en el estado de la cadena de bloques. Cada bloque sucesivo depende del anterior en una cadena basada en algoritmos de hash.
En general, es básico, pero conviene tenerlo en cuenta: hay que pagar por cada estornudo si los datos cambian. Por cierto, la implementación de un contrato también es un registro en la cadena de bloques y no es barato.
En el mundo backend, estoy acostumbrado al siguiente ciclo de vida de desarrollo de funciones:
Es decir, estamos acostumbrados a trabajar de esta manera, y es gratis. Aunque con condiciones, porque pagamos por los servidores. ¿Qué pasa con blockchain?
En el caso de la cadena de bloques, necesitamos escribir el nuevo código de nuestra “aplicación” (contrato inteligente) en la cadena de bloques. Como escribí anteriormente, tenemos que pagar por cada registro. Antes de realizar una transacción con nuestro contrato inteligente, necesitamos realizar una transacción con la ubicación del contrato inteligente.
Luego, el cliente/servidor de servicio contactará a cualquiera de los nodos para recibir o guardar información en el contrato.
Es necesario notificar a una gran cantidad de nodos: “chicos, aquí está el bytecode del contrato cuyos algoritmos deben ejecutarse en mis transacciones”. Es imperativo asegurarse de que el mismo código aparezca en todos los nodos que aprenden la cadena de bloques y se ejecute de la misma manera, independientemente de quién lo llame y de cómo se llame. La mecánica será la misma e invariable. Además, no hay forma de que el contrato inteligente pueda modificarse de alguna manera para que funcione de manera diferente en cualquiera de los nodos.
A continuación se muestra un ejemplo de una transacción en la que deposité un contrato en la red ETH hace bastante tiempo.
Fue un contrato de prueba que nunca se usó en la vida real. Pagué 200 dólares en ETH para implementarlo. Es decir, todavía no habíamos hecho nada con este contrato: ni una sola solicitud, pero ya se habían gastado 200 dólares. Todavía me entristece recordar esta implementación errónea del contrato equivocado...
Hablemos del almacenamiento de datos. Todos estamos acostumbrados a tener PostgreSQL , MySQL , MongoDB , Redis y otros servicios en el backend que nos permiten trabajar con datos de manera conveniente. En el caso de blockchain, no hay nada parecido.
En la cadena de bloques, el almacenamiento se implementa como las variables en una clase en otros lenguajes. Es decir, solo valores clave o matrices. No hay tablas relacionales con enlaces convenientes, etc. Simplemente, escriba en una variable y sea feliz.
Por el momento, no conozco otra forma de organizar el almacenamiento en blockchain. Bueno, tal vez la situación ya haya cambiado; tal vez cuando estés leyendo esto, exista una forma de hacerlo. Escríbela en los comentarios.
Por ejemplo, si no queremos almacenar solo en una matriz, sino que queremos almacenar información por clave, existe un mapeo para eso.
El signo de dólar se dibuja por una razón: se tomará la comisión de la red por cada conjunto.
En este bloque hablaré de cosas que me sorprendieron o me enojaron. Hay muchas más de las que aparecen en este documento, pero compartiré las primeras cosas que me impactaron en mi práctica.
Es importante señalar que la mayoría de los “dolores” son comprensibles por alguna razón lógica, pero eso no anula los dolores que siento en mi cerebro.
Por ejemplo, estoy acostumbrado al hecho de que podemos recorrer fácilmente todos los elementos de cualquier cosa. No importa si se trata de una matriz, un objeto o un mapa. En Solidity, para este propósito, tendremos que almacenar por separado una matriz de todas las claves y luego, si es necesario, recorrerlas todas y recuperar elementos del mapa para cada clave. Bueno, también gastamos gas en escritura adicional en esta matriz de claves y su inicialización.
Tampoco podemos conseguir todo el material práctico para ordenar llaves.
La situación con el registro también es desagradable. Estoy acostumbrado a depurar mediante el depurador en el entorno de desarrollo, pero aquí debería olvidarse incluso del registro normal.
En Typescript, estoy acostumbrado a escribir console.log(a)
y obtener inmediatamente la salida en la consola. En Solidity, existe console.log
, que funciona solo cuando se ejecuta en el entorno de desarrollo local de hardhat . Y lo bueno es que después de dividir lo que necesito, tengo que eliminar todos estos registros antes de que se implemente el contrato porque, de lo contrario, el contrato pesa más y cuesta más implementarlo, y no funcionará en absoluto en la producción.
Al final resulta que cuando ejecutamos el proyecto ya en batalla, queremos ver qué está mal, no podemos ver qué salió mal. Pero sí podemos ver qué salió bien. Hay un sistema de eventos dentro de los contratos inteligentes. Aquí hay un ejemplo: digamos que queremos tener un evento en el que se agregó un nuevo elemento bajo este índice con este valor.
Llamamos a este evento dentro del método set
y solo podemos ver los registros cuando se ejecuta correctamente. Si algo salió mal, si se realizaron múltiples llamadas a contratos o si se produjo una falla en la transacción, los registros tampoco se guardan porque la información en la cadena de bloques se revierte.
Supongamos que utiliza una cadena de varios contratos inteligentes. En el primer contrato se han llamado algunos eventos, luego se llama al segundo contrato, que llama a otros eventos, y luego todo lo que se llamó dentro del segundo contrato cae. Todo se eliminará exactamente por completo de una vez por todas.
Tenemos que ser muy cuidadosos cuando queremos registrar lo que sucede dentro de la cadena de bloques y tener en cuenta que el registro normal, al que estamos acostumbrados, simplemente no está disponible para nosotros aquí.
Otra cosa desagradable es que no podemos obtener información de nuestras transacciones en una función de escritura. Si realizamos una transacción que escribe algo en la cadena de bloques (es decir, una transacción paga), el return
no le dará nada a nuestro servicio que se integra con el contrato inteligente. Este retorno solo funciona dentro del contrato inteligente en sí o en funciones view
(gratuitas).
Por ejemplo, nos gustaría que cuando agreguemos un nuevo valor a nuestra cadena de bloques, queramos averiguar el tamaño de almacenamiento después de guardarlo (captura de pantalla anterior). Es decir, solo podemos averiguar a través de eventos qué se agregó exactamente. Y para hacer eso, necesitamos extraer los eventos que se activaron dentro de esa transacción.
Me llevé una sorpresa: no es posible trabajar con cadenas de forma normal. La cadena de bloques no fue creada para cadenas. Pasemos a los ejemplos.
El código siguiente funcionará sin ningún problema.
Y este código ya no funcionará:
Hace tiempo que estoy acostumbrado a trabajar normalmente con cadenas, cambiar caracteres en cadenas, cortar cadenas, concatenarlas; todo esto no está disponible de fábrica. Tampoco existe la posibilidad de mostrar la longitud de la cadena. Es decir, este código no se compilará:
Si realmente necesitas la longitud de una cadena, puedes convertirla a bytes y luego contar la cantidad de bytes. Pero el problema es que algunos caracteres especiales no se convierten a bytes 1 contra 1. Y algunos simplemente no se convierten y la transacción puede fallar.
Podrías terminar escribiendo un contrato inteligente que maneja cadenas y realiza pruebas en cadenas normales. Luego, llegará una cadena que no se procesará y todo fallará o la longitud de la cadena se contará incorrectamente debido a caracteres especiales.
La conclusión sobre las cadenas es simple: no trabaje con cadenas y no dependa de ellas dentro del contrato. Si es importante guardar cadenas, guarde bytes y confíe en ellos, y convierta las cadenas en bytes en el propio servicio.
La siguiente complejidad, que es una extensión de la característica principal de la cadena de bloques, es el aislamiento. Todos los datos que residen en la cadena de bloques nacen dentro de la cadena de bloques o se transmiten a ella desde el exterior. Pero la cadena de bloques en sí misma nunca puede tocar el mundo exterior, solo otros contratos inteligentes.
El problema es que todos los comandos de los contratos inteligentes se ejecutan en cada participante de la red y no se puede confiar en una fuente externa, ya que no se puede estar seguro de que se recibirá la misma información en cada nodo. Ocurrirá que cada nodo tendrá una versión diferente de la cadena de bloques con datos diferentes y la cadena de bloques colapsará.
Y la tarea trivial de “obtener la temperatura actual en el exterior” se vuelve algo imposible. Si bien no siempre necesitamos el clima, algunos datos (como los tipos de cambio de divisas o el estado actual de algún sistema externo) son esenciales. La solución está en el siguiente enfoque:
Resulta que es una cadena muy larga. Lo triste de la historia es que el dinero se toma en mi primera solicitud del formulario "ir por mí en tal solicitud" y en la segunda solicitud, que ya está hecha por el servidor que ha ejecutado la solicitud.
Por ejemplo, nos cuesta 50k de gas por cada paso. Iniciamos la transacción, ponemos 50k de LÍMITE DE GAS y pensamos que estaremos bien. Pero, por ejemplo, la mecánica de guardar el nuevo clima cambia: ahora, cuando la temperatura sea superior a los 10 grados, debemos transferir dinero a uno de los participantes. La lógica se expande y ahora se necesitarán, por ejemplo, 80k de gas por transacción.
Al final, ya en la segunda transacción, toda la cadena colapsa por falta de gas para la transacción. Este tipo de “huerto” en torno a las llamadas externas hace que estos proyectos sean más complicados. Lo más probable es que, si tienes una conexión sólida con el mundo exterior, no debas elegir blockchain para tu proyecto.
Tampoco existe una aleatoriedad normal que no pueda predeterminarse. Esta aleatoriedad también la ofrecen distintos proveedores “tal cual”, es decir, se escribe regularmente un valor aleatorio en el contrato inteligente. Pero es peligroso confiar en algo así para proyectos financieros reales.
Merece especial atención el hecho de que el valor de la variable block.timestamp
lo establezca el minero del bloque. Por supuesto, es difícil imaginar que el minero sepa de antemano que es él quien extrae el bloque y que pueda sustituir la hora. Aun así, existe una posibilidad hipotética. Este peligro es relevante en el contexto de 15 segundos, y si nos basamos en minutos e intervalos de tiempo amplios, no existe tal problema.
No tengo intención de hablar mucho de seguridad, pero sí de destacar un aspecto importante: todo lo que hay en la blockchain es visible para todos. Lo único que es inaccesible para los demás es tu clave privada. El código del contrato inteligente se pone a disposición del público para que pueda pasar las auditorías y los usuarios del contrato inteligente puedan confiar en él.
El procedimiento de auditoría implica contratar a una empresa para que examine el código del contrato inteligente y verifique que este contrato en particular esté publicado en esta dirección. Se comprueba la cuestión de la seguridad del contrato y se hace lo que declaran los desarrolladores. A continuación, la empresa auditora publica información como "este contrato ha sido verificado por nosotros, se puede confiar en él" en su sitio web.
Pero incluso si no se proporciona el código del contrato, se puede descompilar fácilmente. Por ejemplo, el código siguiente tiene una variable inmutable: simplemente se reemplaza en todas partes por una constante en el código siguiente.
Después de implementar este contrato y abrirlo a través del descompilador, vemos lo siguiente:
Es decir, obtenemos este valor de la variable inmediatamente.
Estoy acostumbrado a poder estar tranquilo en el backend, y el valor de las variables privadas para leer sin acceso a la memoria será problemático. Aquí sucede lo mismo, solo que todos tienen acceso a la "memoria".
Llamamos privada a la variable amount
. Implemente el contrato inteligente y luego extraiga su valor con un fragmento de código simple:
De esa manera, puedes terminar extrayendo cualquier cosa, así que no pienses en almacenar nada confidencial en el contrato inteligente.
Es básicamente imposible revertir los cambios. Un contrato inteligente se desasigna una vez y no se puede cambiar nada. Permanecerá en la cadena de bloques hasta el fin de los tiempos y más allá.
Por eso hay que escribir todo correctamente y bien a la vez. Yo no puedo hacer eso, así que rápidamente se me ocurrió una solución interesante: los contratos actualizables . Su mecánica funciona de la siguiente manera.
Se publica la primera versión del contrato (Contrato V1)
Se publica un contrato proxy y tiene la siguiente tarea: reenviar todas las solicitudes 1v1 al contrato V1 o usar su propio almacenamiento y usar solo la lógica del contrato de destino.
Además, el usuario se comunica con el contrato proxy de la misma manera que con el principal.
Si es necesario actualizar el contrato, el administrador implementa el Contrato V2 y, a través de admin-contract, le dice a proxy-contract que la implementación ahora está en la dirección del Contrato V2.
A continuación, el usuario también se comunica con el proxy y ya se ejecuta la mecánica del Contrato V2.
A continuación, el usuario también se comunica con el proxy y la mecánica del Contrato V2 ya está ejecutada.
Este mecanismo tiene una serie de limitaciones y trucos. Por ejemplo, las variables de la versión anterior no se pueden cambiar en la nueva versión del contrato. Si una variable ya no es necesaria, se debe dejar y despoblar en el nuevo contrato.
Por supuesto, esta solución y muchas otras ya cuentan con soluciones alternativas ya preparadas. El principal proveedor de estos desarrollos es OpenZeppelin . Por lo tanto, afortunadamente, no es necesario reinventar la rueda.
Contrato actualizable:
Los contratos inteligentes actualizables son una gran razón para no someterse a una auditoría. El mundo de la cadena de bloques se basa en la confianza. Ahora bien, un contrato inteligente puede tener una mecánica honesta y abierta, pero más adelante, el propietario del contrato inteligente cambiará la implementación a una en la que él se quede con todo el dinero.