Animagus Parte 1: Introducción

Escrito por Xuejie Xiao

Esta es la primera parte de una serie que presentará y explicará Animagus , el problema que pretende resolver y cómo puedes aprovecharlo al máximo.

Creemos que Nervos Common Knowledge Base (CKB) tiene un potencial inigualable entre las blockchains de la generación moderna. Actualmente, se requieren herramientas especialmente construidas para aprovechar todo el potencial de CKB. Animagus es una herramienta así.

El nombre “animago” proviene de la serie de Harry Potter. Una bruja o mago se llama un “animagus” si él/ella puede transformarse en un animal y volver de nuevo. Dependiendo de la bruja o mago específico, el animal en el que pueden transformarse también difiere. Personalmente, creo que el nombre “animago” encaja bien con el ámbito de este proyecto–espero que estés conmigo después de leer esta serie.

En serio, ¿qué es Animagus?

Históricamente ha habido muchas formas de describir lo que hace Animagus. Algunos dirían que es un framework para dApp, algunos podrían decir que es una capa sobre CKB, pero la descripción que me gusta en estos días es “una capa de cuenta para CKB”.

Si bien la mayoría de las blockchains actuales utilizan un modelo de cuenta, CKB usa un modelo similar a UTXO que se hereda de Bitcoin. Si bien el modelo UTXO ciertamente tiene muchas ventajas, hay un inconveniente importante: es más difícil de programar en comparación con el modelo de cuenta.

Aquí es donde entra en juego Animagus . Como veremos en esta publicación, y con publicaciones futuras, Animagus proporciona una solución para la mayoría–si no todos–los obstáculos de programación que uno encuentra en el modelo UTXO. Esperamos que Animagus pueda llenar los vacíos del modelo de programación UTXO y permitir la realización de todos los beneficios de CKB.

En el aspecto técnico, Animagus se puede ver como un AST runner:

  1. Un desarrollador diseña la funcionalidad que desea en un AST o Abstract Syntax Tree. (Si esto de AST le parece extraño, por favor ten paciencia conmigo por un segundo. Por ahora, puedes suponer que es un programa corto)
  2. El AST se expresará en el formato de Protocol Buffers, por lo que Animagus sigue siendo de lenguaje agnóstico.
  3. Al arrancar, Animagus cargas el AST. Basado en la definición de AST, Animagus puede proporcionar diferentes comportamientos:
  4. Puede leer el estado CKB e indexar ciertas células que coinciden con condiciones predefinidas, como todas las células de depósito en Nervos DAO
  5. Puede proporcionar RPCs que un desarrollador puede llamar desde otros servicios para obtener ciertos resultados, como recuperar el saldo de una cuenta o ensamblar una transacción de transferencia
  6. Puede generar contratos inteligentes de validación utilizados en cadena a partir de definiciones AST

Ejemplo
Estoy seguro de que todo esto suena bastante complicado en este momento. Permítanme explicar cómo funciona esto con un ejemplo real: en el modelo UTXO, una cuenta solo se puede ver como el conjunto de UTXO en vivo (o células en la terminología de CKB). El saldo de una cuenta en CKB es solo la suma de capacidades de todas las células vivas del conjunto.

El núcleo CKB nunca ha proporcionado un RPC para recuperar el saldo de una cuenta arbitraria. Sí, el indexador del nodo CKB tiene una forma de recuperar el saldo actual de las celdas indexadas, pero ¿qué sucede si queremos obtener el saldo de cualquier cuenta aleatoria? Aquí es donde Animagus puede ayudar: vamos a diseñar un AST aquí, de modo que cuando se inicie Animagus, proporcione un RPC que se puede usar para obtener el saldo de CKB de cualquier cuenta.

Para que nuestro ejemplo se adapte mejor a la introducción a Animagus, creemos también las siguientes restricciones:

  1. Nuestro AST solo admite cuentas que usan el script de bloqueo predeterminado de CKB.
  2. Realmente es el args que define la cuenta a la que pertenece una célula. En este caso, escribiremos la construcción RPC para que acepte una serie de bytes, el RPC devolverá el saldo de la cuenta cuando tratemos la serie de bytes como args (recuerda que cuando usas el script de bloqueo predeterminado code_hash y hash_type son valores fijos).

Como se mencionó anteriormente, Animagus es un lenguaje agnóstico. Si bien Animagus está escrito en Go, puede usar cualquier idioma para crear el AST y para llamar a Animagus, siempre que GRPC sea ​​compatible con el idioma de tu elección. En nuestro ejemplo de esta serie, usaremos Ruby para construir el AST y llamar a Animagus, pero no está limitado a esto.

Se requieren 2 partes para construir un Animagus AST: consulta y transformación.

Consulta

La consulta en Animagus tiene 2 propósitos:

  • Indexer utiliza consultas para indexar las células necesarias
  • El servidor RPC utiliza la parte de consulta para recuperar las células necesarias para servir el RPC.

En nuestro ejemplo de saldo, la consulta realmente consulta todas las células que pertenecen a una cuenta específica, pero esto depende completamente del AST definido. Un AST diferente podría estar consultando células de varias cuentas o células que tienen otras cosas en común. Más adelante en la fase de transformación, extraeríamos capacidades de todas las células consultadas y haríamos una suma para obtener el balance final.

Para definir una parte de consulta en Animagus, todo lo que tenemos que hacer es definir una función que devuelva true las células que queremos consultar, y devuelve false lo contrario. El siguiente fragmento muestra cómo podemos definir dicha función en Ruby:

PARAMS = [“0x12345678ab12345678ab12345678ab12345678ab”]
def filter(cell)
cell.lock.code_hash == " 0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" &&
cell.lock.hash_type == “type” &&
cell.lock.args == PARAMS[0]

Usamos PARAMS para denotar los parámetros proporcionados por los clientes RPC. En este caso, la solicitud RPC acepta un parámetro, que es la args parte del script que indica el propietario de la cuenta. Para que esto se parezca más a un AST, podemos modificar el código Ruby aquí un poco:

PARAMS = [“0x12345678ab12345678ab12345678ab12345678ab”]
ARGS = [cell]
def filter
ARGS[0].lock.code_hash == " 0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" &&
ARGS[0].lock.hash_type == “type” &&
ARGS[0].lock.args == PARAMS[0]

Si bien normalmente no escribiremos código de esta manera, se parece más a un AST real: los nodos especiales ARGS y PARAMS se usan para representar argumentos y parámetros pasados ​​a la función (ya sea por el nodo AST principal o por los usuarios de RPC).

Consejo: observa que se usan 2 argumentos: usamos ARGS para denotar un nodo AST especial de Animagus, lo que representa que una parte de AST es en realidad una función. ARGS será llenado por el nodo padre al ejecutar el AST; mientras que script args representa la parte args en la estructura de script de CKB, que el usuario completa para indicar el propietario de la cuenta.

Ahora podemos construir un AST basado en el fragmento de código anterior:

Esto es lo que AST significa: es una representación en árbol de un fragmento de código, donde los valores en el código se convierten en un nodo en el árbol, y las operaciones / funciones en el código también se convierten en nodos con argumentos como nodos secundarios. AST se usa ampliamente en tecnologías de compilación y casi todos los lenguajes de programación que usamos se analizan primero en un AST antes de que se ejecuten más operaciones.

Cuando tenemos la función AST de consulta, podemos alimentarla a un QueryCell nodo especial , que luego completa la parte de consulta.

Por consideraciones de espacio, hemos omitido ciertos nodos secundarios que ya existen en el gráfico AST anterior.

¿Por qué AST?
Una pregunta que puedes tener es ¿por qué usar AST directamente aquí? ¿Por qué no simplemente construir un pequeño lenguaje de programación? De hecho, pensamos en esto en la fase de diseño, pero finalmente votamos en contra por varias razones:

  • Existen demasiados lenguajes de programación, ya sean lenguajes de programación de propósito general o lenguajes enfocados en contratos inteligentes, realmente parece que todos están ocupados implementando su propio lenguaje y cada persona tiene sus propias preferencias cuando se trata de sintaxis. El resultado es que cada vez que alguien quiere elegir una nueva blockchain, lo primero que debe hacer es aprender un nuevo lenguaje de programación. Queremos cambiar esto, por lo que solo estamos definiendo el AST central que tenemos aquí. Todos son libres de implementar el estilo de idioma que quieran y compilar su mini idioma en Animagus AST. De esta manera, podemos garantizar la máxima libertad al usar Animagus.
  • En realidad, es más que solo lenguajes de programación que se compilan directamente en AST. Las personas con antecedentes de aprendizaje profundo sabrán que los gráficos de cálculo y las DSL son muy populares en ciertos campos. En esas situaciones, puedes continuar usando tu lenguaje existente y usar todo tipo de DSL para construir la lógica que necesitas. Más tarde, puedes exportar la lógica compleja definida a través de DSL y ejecutarla en GPU modernas. De hecho, más adelante mostraremos exactamente cómo puedes definir Animagus AST de esta manera, en Ruby simple.
  • En realidad, hay más en la capacidad de un AST que solo lenguajes de programación. En diferentes campos, muchas personas también están construyendo cosas increíbles sin programación. Por ejemplo, los talentosos diseñadores de juegos están creando todos los días nuevos juegos increíbles a través de cosas como Unreal Blueprint. Estos desarrolladores no necesitan lenguajes de programación para cumplir con su elección de la mecánica del juego. Creemos en un futuro diverso, al proporcionar Animagus AST directamente, también podríamos habilitar la construcción de dapp de estilo blueprint, donde las personas con talento sin habilidades de programación también pueden crear el dapp que necesitan.

En pocas palabras, al igual que CKB proporciona la máxima flexibilidad al mundo blockchain, creemos que un diseño basado en AST también proporciona la máxima flexibilidad a Animagus, que desbloquea muchas más posibilidades.

Transformando

Con una parte de consulta en su lugar, podemos comenzar a construir la fase de transformación. Se necesitan 2 funciones más en la fase de transformación. El primero se usa para extraer capacidad de una célula:

El segundo solo agrega 2 valores de capacidad juntos:

Como ha visto, no todos los AST tienen que ser súper complejos, algunos pueden ser realmente simples siempre y cuando cumplan con sus propósitos.

Ahora podemos ensamblar la parte transformadora juntos:

Hay 2 nuevos tipos de nodos en el gráfico:

  • “Map” acepta una lista (en este ejemplo, la lista de células devueltas desde el nodo QueryCell) y una función. Aplica la función a cada uno de los elementos de la lista y devuelve la lista resultante.
  • “Reduce” acepta una lista, una función y un valor inicial. Aplica la función con el valor inicial y el elemento, usa el valor de retorno para reemplazar el valor inicial y continúa hasta que se agota los elementos de la lista. El resultado de “reduce” es un valor único.

Si estas familiarizado con la programación funcional, estas son exactamente las funciones simples de ‘map/reduce’ que te encantarán. El nodo de reducción contiene el valor transformado, que es la capacidad total de todas las células en la cuenta especificada.

Juntando

El trabajo restante aquí es trivial, le damos un nombre al método RPC, lo juntamos en un nodo Call y luego creamos un nodo Root que lo contiene:

Cuando Animagus arranque, acepta un mensaje protobuf serializado del nodo Root. Un nodo Root puede tener múltiples nodos Call (más adelante veremos que también puede contener múltiples nodos Stream, pero esto está fuera del ámbito de esta publicación inicial). Animagus analiza cada nodo Call y realiza cada operación:

  • Extrae todos los nodos QueryCell de todos los nodos Call (ten en cuenta que está permitido que un nodo Call tenga múltiples nodos QueryCell). Para cada nueva célula en cada bloque, Animagus ejecuta la célula contra la función descrita en el nodo QueryCell. Si la función regresa true, Animagus almacenará un registro de la célula (también conocido como indexar la célula).
  • Cuando se realiza una solicitud RPC a Animagus, Animagus localiza el nodo Call correspondiente y ejecuta el AST. Cuando QueryCell se encuentra un nodo, buscará las células correspondientes en función de los registros de indexación.

Como se muestra en el ejemplo anterior, hemos implementado una forma de indexar células y proporcionar llamadas RPC para obtener los saldos de CKB de cada cuenta.

Conclusión

Al diseñar Animagus, comencé a darme cuenta de que desde una perspectiva de programación, el modelo de cuenta y el modelo UTXO podrían no ser tan diferentes. Mientras que el modelo de cuenta proporciona el estado de la cuenta directamente, el modelo UTXO simplemente extiende el estado de la cuenta a través de un conjunto de UTXO (o células activas en el caso de CKB). No hay nada que impida el uso de herramientas como Animagus para ayudar a organizar y transformar los UTXO en una forma que sea directamente legible, al igual que el modelo de cuenta.

Espero que no te aburras con esta primera publicación :slight_smile: En la próxima publicación te mostraré cómo puedes construir el saldo AST en Ruby y ejecutarlo de verdad en Animagus.

Únete a nuestra comunidad: Github Forum Reddit Twitter

Para discusiones o preguntas, únete a la conversación en Discord o en uno de los canales de Telegram de nuestra comunidad: inglés , coreano , ruso , japonés , español , vietnamita y chino

1 Like