
En el último artículo
"Agentes autónomos" o cuando ejecutamos el código en la criptoplataforma abierta Obyte, hablamos sobre qué son los Agentes autónomos y los comparamos con los contratos inteligentes de Ethereum. Ahora escribamos nuestro primer Agente Autónomo (AA) usando el ejemplo de 51% de ataque. Y al final del artículo, analizaremos formas de mejorarlo: cómo proteger a los jugadores de la pérdida / pérdida de fondos y cómo mejorar el algoritmo para reducir el efecto de las "ballenas" con grandes depósitos en el resultado del juego.
Los originales de ambos agentes independientes siempre están disponibles en el
editor de código Oscript en
línea en forma de plantillas, solo selecciónelos en el menú desplegable:
"Juego de ataque del 51%" y
"Proxy de recaudación de fondos".Antes de comenzar a escribir AA en Oscript, le recomiendo leer la
Guía de inicio (eng) en nuestra documentación para familiarizarse rápidamente con los principios básicos de la escritura de AA.
La esencia del juego
Varios equipos compiten simultáneamente entre sí por el derecho a recaudar todo el conjunto de fondos recaudados. Los jugadores del equipo hacen depósitos, lo que aumenta la cantidad total del grupo. El equipo que ha realizado depósitos de al menos el 51% de todos los fondos recaudados y mantenido como líder durante al menos un día gana. El grupo se distribuye entre los jugadores del equipo ganador en proporción a la contribución realizada, por lo que cada participante puede potencialmente duplicar su inversión.
Tan pronto como cualquiera de los equipos recolecta> = 51% de todos los fondos, se declara previamente ganador y sus participantes ya no pueden realizar depósitos. Pero todos los otros equipos tienen 24 horas para superar al equipo ganador. Tan pronto como esto suceda, el equipo de adelantamiento se convierte en el ganador y el temporizador comienza la cuenta regresiva nuevamente.
Nuestra implementación emitirá "acciones" a cada depositante a cambio de bytes, en una proporción de 1 a 1, que, si el equipo gana, el participante puede cambiar por una acción en el grupo ganado. Las acciones son un activo en la plataforma Obyte, lanzadas específicamente para cada equipo. Ellos, como cualquier activo, pueden ser transferidos / intercambiados, vendiendo sus ganancias potenciales a otras personas. El precio de la acción dependerá de que el mercado evalúe las posibilidades de ganar del equipo.
Cualquiera puede crear un equipo. El creador del equipo puede establecer una comisión por ganar, que se cobrará a cada miembro del equipo en caso de victoria.
También aplicaremos el segundo AA en nuestro juego, en nombre del creador del equipo, que "financiará en masa" la cantidad necesaria, y solo si se logra (en nuestro ejemplo, cuando alcanza> 51% del grupo de juego) enviará todos los fondos recaudados a la dirección AA juegos, de lo contrario el dinero puede ser devuelto libremente.
Escribir código oscript
Permítame recordarle que el código AA se llama cada vez que una transacción llega a la dirección de este AA, el llamado desencadenar transacción. En realidad, AA es un código que, en respuesta a la entrada (datos en una transacción de activación y el estado actual de AA, almacenado en variables de estado) genera salida (otra transacción de "respuesta") o cambia su estado. Nuestra tarea es programar las reglas de respuesta de AA a los datos de entrada. Cualquier transacción en Obyte es un conjunto de mensajes, la mayoría de las veces son mensajes de "pago" o mensajes de "datos", etc.
Inicialización
Primero, inicializamos nuestro AA. El bloque init se llama cada vez que se inicia AA, desde el principio. En él estableceremos constantes locales para un acceso más conveniente a los valores.
{ init: `{ $team_creation_fee = 5000; $challenging_period = 24*3600; $bFinished = var['finished']; }`, messages: { cases: [ ] } }
Cadena
$ bFinished = var ['terminado']; lee la variable
terminada del estado AA.
La matriz de los mensajes resultantes estará enmarcada por los
casos: bloque
{} , que es similar al
interruptor habitual
/ caso / bloque
predeterminado , en función de las condiciones en los bloques secundarios, solo se seleccionará uno de los mensajes, o si no,
si los bloques devuelven
verdadero , será último mensaje seleccionado.
Creamos un equipo
Entonces, el primer bloque procesará las transacciones para crear un nuevo comando:
Como vemos, la condición para la ejecución de este bloque es la primera en el bloque if y es una verificación de que la transacción desencadenante contiene un mensaje con el tipo de datos (
trigger.data.create_team ), que tiene una clave llamada
create_team y que el juego aún no ha terminado (
! $ bFinished ). La constante local
$ bFinished es accesible desde cualquier parte del código si se ha inicializado en el bloque init. Si al menos algunas de estas condiciones no se cumplieran, entonces el bloque primario de casos simplemente continuaría cumpliéndose y verificando las condiciones para los siguientes mensajes, omitiendo este.
En el siguiente bloque de
inicio , no inicializamos nada, pero verificamos las condiciones necesarias, sin las cuales la transacción desencadenante se considera errónea:
if (var['team_' || trigger.address || '_asset']) bounce('you already have a team'); if (trigger.output[[asset=base]] < $team_creation_fee) bounce('not enough to pay for team creation');
Aquí concatenamos (usando ||) la cadena con la variable del activador de la transacción e intentamos averiguar si hay una variable llamada
'equipo_' || trigger.address || '_asset' en nuestra historia de AA.
La llamada
bounce () revierte cualquier cambio realizado en el momento actual y devuelve un error a la persona que llama.
Observe también cómo se realiza la búsqueda dentro de la transacción desencadenante:
trigger.output [[asset = base]] está buscando resultados con
asset == base , que devolverá la cantidad en bytes (activo base = bytes) que se especificó en la transacción desencadenante. Y si esta cantidad no es suficiente para crear un nuevo equipo, llamamos a bounce (), comiendo silenciosamente todos los bytes entrantes menos bounce_fee, que por defecto es de 10,000 bytes.
A continuación, comienza la mayor parte del código de creación de equipo. Brevemente, el algoritmo es el siguiente:
- El primer mensaje lanza un nuevo activo ( aplicación: 'activo' )
- El segundo mensaje devuelve todo lo que es más que el número requerido de bytes para crear el comando ( aplicación: 'pago' ). Echa un vistazo al bloque if aquí. Si esta condición es falsa (el creador envió exactamente el número requerido de bytes), entonces este mensaje no se incluirá en la transacción resultante, sino que simplemente se descartará.
- El tercer mensaje cambia el estado de nuestro AA ( aplicación: 'estado' ): escriba el fundador_tax pasado como argumento, o configúrelo en 0 si no se pasó a la transacción de activación. La construcción var1, de lo contrario, var2 devuelve var1 si se convierte en verdadero; de lo contrario, devuelve var2. Aquí nos encontramos con la variable response_unit, que siempre contiene el hash de la unidad resultante. En este caso, porque la unidad resultante creará un nuevo activo llamado activo-a y será el hash de la unidad creadora. La respuesta de cadena ['team_asset'] = response_unit simplemente escribe el mismo hash (o activo para el comando dado) en la matriz responseVars en la unidad final. La matriz de respuestas también se puede leer a la persona que realizó la transacción desencadenante, así como a los oyentes de eventos suscritos a eventos con este AA.
Aceptamos depósitos
Una vez finalizada la creación del equipo, pase al siguiente bloque: procesar depósitos de los miembros del equipo.
De lo nuevo que encontramos aquí es la emisión de tokens del activo del equipo seleccionado a su participante a cambio de su depósito en bytes:
asset: `{var['team_' || trigger.data.team || '_asset']}`, outputs: [{address: "{trigger.address}", amount: "{trigger.output[[asset=base]]}"}
Como recordamos, la variable de estado
'equipo_' || trigger.data.team || Guardamos el
'activo' en la etapa de creación del equipo, y almacena el hash de la unidad en la que creamos el activo para este equipo, es decir, el nombre de este activo-a.
En el mismo bloque, la condición principal se verifica para el 51%:
if (var['team_' || trigger.data.team || '_amount'] > balance[base]*0.51){ var['winner'] = trigger.data.team; var['challenging_period_start_ts'] = timestamp; }
Si después de esta transacción de activación, el saldo del equipo especificado supera el 51%, entonces el equipo se declara ganador y registramos la marca de tiempo de Unix actual (inicia el temporizador).
Esta marca de tiempo se verificará en el tercer bloque cuando recibamos una transacción de activación con un intento de finalizar el juego:
Pagamos un premio
Y el bloque final, el más divertido, es el pago del depósito completo a los ganadores:
El bloque de inicialización es interesante aquí, en el que calculamos los valores necesarios por adelantado:
$share = $asset_amount / var['team_' || $winner || '_amount']; $founder_tax = var['team_' || $winner || '_founder_tax']; $amount = round(( $share * (1-$founder_tax) + (trigger.address == $winner AND !var['founder_tax_paid'] ? $founder_tax : 0) ) * var['total']);
Todos los participantes del equipo, excepto su creador, reciben un monto proporcional a su contribución inicial (1 por 1 bytes a cambio de los tokens de activos enviados en una transacción de activación). El creador también recibe una comisión (se verifica que la dirección de la
persona que envió la transacción de activación es igual a la dirección del creador del equipo ganador
trigger.address == $ ganador ). Es importante no olvidar que la comisión debe pagarse solo una vez, y el creador puede enviar infinitas transacciones desencadenantes, por lo que guardamos la bandera en el estado AA.
Comienza el juego
Entonces, el código está listo. Aquí hay una lista completa de la misma:
código AA completo { init: `{ $team_creation_fee = 5000; $challenging_period = 24*3600; $bFinished = var['finished']; }`, messages: { cases: [ {
Verifiquemos la validez del código e intentemos implementarlo en testnet.
- Vaya al editor en línea: https://testnet.oscript.org
- Pegue nuestro código y haga clic en Validar. Si todo es correcto, veremos un cálculo de la complejidad del código: AA validado, complejidad = 27, ops = 176 . Aquí ops es el número de operaciones en nuestro código, la complejidad es la complejidad del código. Los AA de Obyte no tienen ciclos, pero incluso esto no permite una protección del 100% de la red contra los AA maliciosos con código incorrecto. Por lo tanto, todos los AA tienen un límite superior de complejidad, complejidad = 100. La complejidad del código se calcula en el momento de la implementación y se tienen en cuenta todas las ramas del código. Algunas operaciones son relativamente fáciles, como ± y otras, pero no agregan complejidad. Otros, como el acceso a la base de datos (modificación del estado) o cálculos complejos (que llaman a algunas funciones) agregan complejidad. Para averiguar qué operaciones son fáciles y cuáles son complejas, consulte la referencia del idioma .
- Haz clic en Implementar. Vemos algo similar a
Check in explorer: https://testnetexplorer.obyte.org/#DiuxsmIijzkfAVgabS9chJm5Mflr74lZkTGud4PM1vI= Agent address: 6R7SF6LTCNSPLYLIJWDF57BTQVZ7HT5N
Le aconsejo que siga el enlace en el explorador y asegúrese de que la unidad con nuestro AA esté publicada en la red. Explorer también muestra el código completo de nuestro AA, como Todos los AA en la red Obyte son de código abierto. Puede estudiar el código de cualquier agente en su dirección.
Crowdfunding
Y ahora a las optimizaciones prometidas. En la implementación actual, cada jugador envía dinero inmediatamente a la dirección AA del juego, indicando su equipo. Al mismo tiempo, el equipo nunca puede convertirse en un líder, y el dinero ya ha sido enviado. Podemos optimizar el proceso de recolección de dinero y evitar la situación de que enviemos dinero a un equipo que nunca se convertirá en un ganador. Esto se puede hacer con el segundo AA, organizar el llamado crowdfunding de fondos estableciendo un objetivo dinámico para la cantidad de fondos recaudados igual al 51% de la cantidad en el juego.
En Oscript, podemos leer el estado de cualquier otro AA, por lo que tenemos la oportunidad de establecer un objetivo dinámico en nuestro AA de crowdfunding.
El algoritmo será el siguiente: el creador del equipo pide a los jugadores que envíen dinero no a la dirección del juego, sino a la dirección de un agente que implementa la funcionalidad de crowdfunding. Este agente almacenará la cantidad recolectada de bytes en casa y los enviará al juego solo si recolecta> = 51% de la cantidad en el juego. E inmediatamente este equipo se convierte en un líder. Si no se recauda la cantidad necesaria, el dinero simplemente se devolverá a los jugadores. En la etapa de recaudación de fondos, los jugadores recibirán no fichas de juego de equipo, sino fichas de crowdfunding, que en el futuro pueden ser reembolsadas o intercambiadas por fichas de juego, si tienen éxito.
En el siguiente artículo, implementamos esta funcionalidad.
No recopilamos bytes, sino certificaciones.
En la forma más simple del juego "Ataque 51%" estamos hablando de las cantidades de fondos recaudados. Por lo tanto, las "ballenas" con grandes billeteras pueden llevarse la mayor parte de las ganancias.
Para que el juego sea más honesto para todos los participantes, trataremos de contar no la cantidad de bytes enviados, sino la cantidad de participantes en los equipos. El equipo que pudo atraer el número máximo de participantes gana. Cada jugador invertirá una cantidad fija, digamos 1GB, y contará como una unidad en el grupo de equipos. Pero nada nos impide crear un número infinito de nuevas direcciones, por lo que la condición crítica será que solo se permitan en el juego las direcciones vinculadas a otras ID, en las que se respeta la regla de "una persona - una ID". Tal enlace se llama
certificación . Un ejemplo de certificación es la aprobación del procedimiento KYC, después del cual se envía un mensaje al DAG sobre la conexión entre la dirección de Obyte y el hash de datos personales (los datos personales se almacenan en la billetera del usuario, y él puede divulgarlos a las contrapartes individuales si lo desea, pero para esta tarea no son necesarios, es importante solo el hecho de la unión). Otro ejemplo de certificación es la certificación de correo electrónico en dominios donde se respeta la regla de "una persona - un correo electrónico", esto se practica, por ejemplo, en los dominios de algunas universidades, empresas y países. Por lo tanto, una persona real se contará solo una vez.
Los agentes autónomos pueden solicitar el estado de certificación de direcciones en la red, por lo que habrá un número mínimo de cambios en el código.
Sugiero a los lectores en los comentarios que sugieran en qué lugares y cómo es exactamente necesario cambiar las líneas de código de la versión actual del juego para implementar esto. Para ayudar, como siempre, a la
Referencia del lenguaje Oscript .
Nuestra
discordia y
Twitter