Probablemente haya escuchado que Telegram lanzará la plataforma de cadena de bloques Ton . Pero podría perderse la noticia de que no hace mucho Telegram anunció una competencia para la implementación de uno o más contratos inteligentes para esta plataforma.
El equipo de Serokell con una rica experiencia en el desarrollo de grandes proyectos de blockchain no pudo mantenerse alejado. Delegamos a cinco empleados al concurso, y dos semanas después ocuparon el primer lugar bajo el apodo aleatorio (des) modesto Sexy Chameleon. En este artículo hablaré sobre cómo tuvieron éxito. Esperamos que en los próximos diez minutos al menos lea una historia interesante y, como máximo, encuentre algo útil que pueda aplicar en su trabajo.
Pero comencemos con una pequeña inmersión en el contexto.
La competencia y sus condiciones.
Por lo tanto, las tareas principales de los participantes fueron la implementación de uno o más de los contratos inteligentes propuestos, así como también hacer propuestas para mejorar el ecosistema TON. La competencia se llevó a cabo del 24 de septiembre al 15 de octubre, y los resultados se anunciaron solo el 15 de noviembre. Durante mucho tiempo, dado que durante este tiempo Telegram logró realizar y anunciar los resultados de concursos sobre diseño y desarrollo de aplicaciones en C ++ para probar y evaluar la calidad de las llamadas VoIP en Telegram.
Seleccionamos dos contratos inteligentes de la lista propuesta por los organizadores. Para uno de ellos, utilizamos herramientas distribuidas con TON, y el segundo lo implementamos en un nuevo lenguaje desarrollado por nuestros ingenieros específicamente para TON e integrado en Haskell.
La elección de un lenguaje de programación funcional no es accidental. En nuestro blog corporativo, a menudo hablamos de por qué consideramos que la complejidad de los lenguajes funcionales es una gran exageración y por qué generalmente preferimos que estén orientados a objetos. Por cierto, también contiene el original de este artículo .
¿Por qué decidimos participar?
En resumen, porque nuestra especialización son proyectos complejos y no estándar que requieren habilidades especiales y que a menudo son de valor científico para la comunidad de TI. Apoyamos calurosamente el desarrollo de código abierto y participamos en su popularización, así como cooperamos con las principales universidades de Rusia en el campo de la informática y las matemáticas.
Las interesantes tareas del concurso y la participación en el proyecto Telegram, que tanto amamos, fueron en sí mismas una excelente motivación, pero el fondo de premios se convirtió en un incentivo adicional. :)
TON Blockchain Research
Monitoreamos de cerca los nuevos desarrollos en la cadena de bloques, la inteligencia artificial y el aprendizaje automático e intentamos no perder una sola versión significativa en cada una de las áreas en las que trabajamos. Por lo tanto, para cuando comenzó la competencia, nuestro equipo ya estaba familiarizado con las ideas del informe técnico de TON . Sin embargo, antes de comenzar a trabajar con TON, no analizamos la documentación técnica y el código fuente real de la plataforma, por lo que el primer paso fue bastante obvio: un estudio exhaustivo de la documentación oficial en el sitio web y en el repositorio del proyecto .
Al comienzo del concurso, el código ya había sido publicado, por lo que para ahorrar tiempo, decidimos buscar una guía o un apretón escrito por los usuarios . Desafortunadamente, esto no dio un resultado, aparte de las instrucciones para construir la plataforma en Ubuntu, no encontramos otros materiales.
La documentación en sí se desarrolló a fondo, pero fue difícil de leer en algunos puntos. Muy a menudo, tuvimos que volver a ciertos puntos y cambiar de descripciones de alto nivel de ideas abstractas a detalles de implementación de bajo nivel.
Sería más fácil si la especificación no tuviera una descripción detallada de la implementación. La información sobre cómo la máquina virtual presenta su stack es más molesta para los desarrolladores que crean contratos inteligentes para la plataforma TON que para ayudarlos.
Nix: construyendo un proyecto
En Serokell, somos grandes admiradores de Nix . Recopilamos nuestros proyectos para ellos y los implementamos usando NixOps , y NixOS está instalado en todos nuestros servidores. Gracias a esto, todas nuestras compilaciones son reproducibles y funcionan en cualquier sistema operativo en el que se pueda instalar Nix.
Entonces comenzamos creando Nix overlay con una expresión para construir TON . Usarlo para compilar TON es lo más simple posible:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix $ cd /path/to/ton/repo && nix-shell [nix-shell]$ cmakeConfigurePhase && make
Tenga en cuenta que no necesita instalar ninguna dependencia. Nix hará mágicamente todo por ti, ya sea que uses NixOS, Ubuntu o macOS.
Programación para TON
El código de contrato inteligente de la red TON se ejecuta en la máquina virtual TON (TVM). TVM es más complicado que la mayoría de las otras máquinas virtuales y tiene una funcionalidad muy interesante, por ejemplo, puede funcionar con continuaciones y enlaces a datos .
Además, los chicos de TON crearon tres nuevos lenguajes de programación:
Fift es un lenguaje de programación de pila universal que recuerda a Forth . Su súper habilidad es la habilidad de interactuar con TVM.
FunC es un lenguaje de programación de contrato inteligente que es similar a C y compilado en otro lenguaje: Fift Assembler.
Fift Assembler : biblioteca Fift para generar código binario ejecutable para TVM. Fift Assembler carece de un compilador. Es un lenguaje incrustado específico del dominio (eDSL) .
Nuestros trabajos competitivos
Finalmente, es hora de ver los resultados de nuestros esfuerzos.
Canal de pago asincrónico
Canal de pago: un contrato inteligente que permite a dos usuarios enviar pagos fuera de la cadena de bloques. Como resultado, no solo se ahorra dinero (no hay comisión), sino también tiempo (no tiene que esperar hasta que se procese el siguiente bloque). Los pagos pueden ser arbitrariamente pequeños y ocurrir con la frecuencia que se requiera. Al mismo tiempo, las partes no necesitan confiar entre sí, ya que la equidad del acuerdo final está garantizada por un contrato inteligente.
Encontramos una solución bastante simple al problema. Dos partes pueden intercambiar mensajes firmados, cada uno de los cuales contiene dos números: el monto total pagado por cada uno de los participantes. Estos dos números funcionan como relojes de vectores en los sistemas distribuidos tradicionales y establecen el orden "sucedido antes" en las transacciones. Con estos datos, el contrato podrá resolver cualquier posible conflicto.
De hecho, para implementar esta idea, un número es suficiente, pero dejamos ambos, ya que pudimos hacer una interfaz de usuario más conveniente. Además, decidimos incluir un monto de pago en cada mensaje. Sin él, si el mensaje se pierde por algún motivo, entonces, aunque todas las cantidades y el cálculo final serán correctos, el usuario puede no notar la pérdida.
Para probar nuestra idea, buscamos ejemplos del uso de un protocolo de canal de pago tan simple y conciso. Sorprendentemente, encontramos solo dos:
- Descripción de un enfoque similar, solo para el caso de un canal unidireccional.
- Un tutorial que describe la misma idea que la nuestra, pero sin explicar muchos detalles importantes, como la corrección general y el procedimiento para resolver conflictos.
Quedó claro que tiene sentido describir nuestro protocolo en detalle, prestando especial atención a su corrección. Después de varias iteraciones, la especificación estaba lista, y ahora usted también puede verla .
Implementamos un contrato para FunC, y escribimos la utilidad de línea de comandos para interactuar con nuestro contrato en Fift, como recomendaron los organizadores. Podríamos elegir cualquier otro idioma para nuestra CLI, pero fue interesante para nosotros probar Fift para ver cómo se muestra en acción.
Honestamente, después de haber trabajado con Fift, no vimos una buena razón para preferir este lenguaje a los populares y utilizados activamente con herramientas y bibliotecas desarrolladas. La programación en el lenguaje de pila es bastante desagradable, porque hay que tener en cuenta constantemente lo que está en la pila, y el compilador no ayuda.
Por lo tanto, la única, en nuestra opinión, la justificación de la existencia de Fift es su papel como idioma anfitrión para el ensamblador de Fift. Pero, ¿no sería mejor incrustar el ensamblador de TVM en algún lenguaje existente y no crear uno nuevo para esto, esencialmente el único propósito?
TVM Haskell eDSL
Ahora es el momento de hablar sobre nuestro segundo contrato inteligente. Decidimos desarrollar una billetera con varias firmas, pero escribir otro contrato inteligente en FunC sería demasiado aburrido. Queríamos agregar algo de entusiasmo, y se convirtió en nuestro propio lenguaje ensamblador para TVM.
Al igual que Fift Assembler, nuestro nuevo lenguaje es integrable, pero en lugar de Fift elegimos a Haskell como el host, lo que nos permitió utilizar completamente su sistema de tipos avanzado. Cuando se trabaja con contratos inteligentes, donde el precio de incluso un pequeño error puede ser muy alto, la escritura estática, en nuestra opinión, es una gran ventaja.
Para demostrar cómo se ve el ensamblador TVM integrado en Haskell, implementamos una billetera estándar. Aquí hay algunas cosas a tener en cuenta:
- Este contrato consta de una función, pero puede usar tanto como desee. Cuando define una nueva función en el idioma del host (es decir, en Haskell), nuestro eDSL le permite elegir si desea que se convierta en un subprograma separado en TVM o simplemente en el lugar de la llamada.
- Al igual que Haskell, las funciones tienen tipos que se verifican en tiempo de compilación. En nuestro eDSL, el tipo de entrada de la función es el tipo de pila que la función espera, y el tipo de resultado es el tipo de pila que se obtendrá después de la llamada.
- El código tiene anotaciones de tipo stack que describen el tipo esperado de stack en el dial peer. En el contrato de billetera original, estos fueron solo comentarios, pero en nuestro eDSL son en realidad parte del código y se verifican en el momento de la compilación. Pueden servir como documentación o declaraciones que ayudan al desarrollador a encontrar el problema si el tipo de pila cambia a medida que cambia el código. Por supuesto, tales anotaciones no afectan el rendimiento del tiempo de ejecución, ya que no se genera ningún código TVM para ellas.
- Este todavía es un prototipo escrito en dos semanas, queda mucho trabajo por hacer en el proyecto. Por ejemplo, todas las instancias de clase que ve en el código a continuación deben generarse automáticamente.
Así es como se ve la implementación de billetera multigrado en nuestro eDSL:
main :: IO () main = putText $ pretty $ declProgram procedures methods where procedures = [ ("recv_external", decl recvExternal) , ("recv_internal", decl recvInternal) ] methods = [ ("seqno", declMethod getSeqno) ] data Storage = Storage { sCnt :: Word32 , sPubKey :: PublicKey } instance DecodeSlice Storage where type DecodeSliceFields Storage = [PublicKey, Word32] decodeFromSliceImpl = do decodeFromSliceImpl @Word32 decodeFromSliceImpl @PublicKey instance EncodeBuilder Storage where encodeToBuilder = do encodeToBuilder @Word32 encodeToBuilder @PublicKey data WalletError = SeqNoMismatch | SignatureMismatch deriving (Eq, Ord, Show, Generic) instance Exception WalletError instance Enum WalletError where toEnum 33 = SeqNoMismatch toEnum 34 = SignatureMismatch toEnum _ = error "Uknown MultiSigError id" fromEnum SeqNoMismatch = 33 fromEnum SignatureMismatch = 34 recvInternal :: '[Slice] :-> '[] recvInternal = drop recvExternal :: '[Slice] :-> '[] recvExternal = do decodeFromSlice @Signature dup preloadFromSlice @Word32 stacktype @[Word32, Slice, Signature]
El código fuente completo de nuestro eDSL y el contrato de billetera con firma múltiple se pueden encontrar en este repositorio. Y con más detalle, nuestro colega George Agapov habló sobre los idiomas incorporados.
Conclusiones sobre la competencia y TON
En total, nuestro trabajo tomó 380 horas (junto con el conocimiento de la documentación, las reuniones y el desarrollo en sí). Cinco desarrolladores participaron en la competencia: STO, líder del equipo, especialistas en plataformas blockchain y desarrolladores de software Haskell.
Encontramos los recursos para participar en el concurso sin dificultad, ya que el espíritu del hackathon, el trabajo en equipo cercano, la necesidad de inmersión rápida en aspectos de las nuevas tecnologías siempre son emocionantes. Unas pocas noches de insomnio para lograr los máximos resultados en condiciones de recursos limitados se compensan con una experiencia invaluable y excelentes recuerdos. Además, trabajar en tales tareas es siempre una buena prueba de los procesos de la empresa, ya que es extremadamente difícil lograr resultados verdaderamente decentes sin una interacción interna excelentemente ajustada.
Aparte de la letra: nos impresionó la cantidad de trabajo realizado por el equipo de TON. Se las arreglaron para construir un sistema complejo, hermoso y, lo más importante, de trabajo. TON demostró ser una plataforma con gran potencial. Sin embargo, para que este ecosistema se desarrolle, se necesita hacer mucho más, tanto en términos de su uso en proyectos de blockchain como en términos de mejorar las herramientas de desarrollo. Estamos orgullosos de ser parte de este proceso ahora.
Si después de leer este artículo todavía tiene alguna pregunta o idea sobre cómo aplicar TON para resolver sus problemas, escríbanos , estaremos encantados de compartir nuestra experiencia.