# AfterCoin (AFC) - Blockchain Privada para el Casino de Minecraft ## Tabla de Contenidos - [Resumen General](#resumen-general) - [Arquitectura](#arquitectura) - [Componentes](#componentes) - [1. Nodo Geth (blockchain/)](#1-nodo-geth-blockchain) - [2. Contrato AfterCoin (blockchain/contracts/AfterCoin.sol)](#2-contrato-aftercoin-blockchaincontractsaftercoinsol) - [3. Bridge API (services/afc-bridge/)](#3-bridge-api-servicesafc-bridge) - [4. Proxy SSL para RPC (docker/nginx/rpc-ssl.conf)](#4-proxy-ssl-para-rpc-dockernginxrpc-sslconf) - [5. Mainframe Lua (Computer 7)](#5-mainframe-lua-computer-7) - [6. Generador de Tarjetas Lua (Computer 4)](#6-generador-de-tarjetas-lua-computer-4) - [Servicios Docker](#servicios-docker) - [Variables de Entorno](#variables-de-entorno) - [Guia de Conexion con MetaMask](#guia-de-conexion-con-metamask) - [Escritorio (Extension)](#escritorio-extension) - [Movil (App)](#movil-app) - [Vincular una Wallet Personal](#vincular-una-wallet-personal) - [Administracion](#administracion) - [Comandos de Verificacion](#comandos-de-verificacion) - [Operaciones Directas con Tokens](#operaciones-directas-con-tokens) - [Renovacion de Certificado SSL](#renovacion-de-certificado-ssl) - [Whitelist HTTP de CC:Tweaked](#whitelist-http-de-cctweaked) - [Despliegue de Scripts Lua](#despliegue-de-scripts-lua) - [Detalles del Contrato](#detalles-del-contrato) - [Solucion de Problemas](#solucion-de-problemas) --- ## Resumen General **AfterCoin (AFC)** es un token ERC-20 desplegado en una blockchain privada de Ethereum que utiliza el mecanismo de consenso **Clique PoA** (Proof of Authority) con chain ID **8888**. Principios fundamentales: - **1 AFC = 1 diamante** en el casino de Minecraft - **0 decimales** (solo valores enteros, sin fracciones) - Los jugadores pueden ver su saldo del casino en **MetaMask** como tokens reales en la blockchain - El **mainframe del casino** sincroniza los saldos on-chain a traves de un **Bridge API** Esto permite que los jugadores tengan una experiencia tangible de sus fondos del casino: pueden abrir MetaMask en su telefono o navegador y ver exactamente cuantos diamantes tienen, con la seguridad y transparencia de una blockchain real. --- ## Arquitectura ``` +---------------------+ +-----------------------------+ | | HTTPS | | | MetaMask |--------->| Nginx SSL Proxy | | (Escritorio/Movil) | :8443 | (rpc-ssl) | | | +-------------+---------------+ +--------+------------+ | | | HTTP :8545 | HTTP :8545 v | (solo escritorio) +--------+---------------+ +--------------------------->| | | Geth Node | | (Clique PoA, ID 8888) | | | +--------+---------------+ ^ | ethers.js (HTTP RPC) | +--------+---------------+ | | | Bridge API | | (Node.js, :3001) | | | | +------------------+ | | | SQLite DB | | | | (wallets, config)| | | +------------------+ | +--------+---------------+ ^ | HTTP (red Docker interna) | +----------------+------------------+ | | | CC:Tweaked Mainframe | | (Computer 7, Minecraft) | | | +---+----------+----------+---------+ | | | rednet rednet rednet | | | +---+--+ +---+---+ +---+---+ | Slots| | BJ | | Poker | +------+ +-------+ +-------+ (Juegos del casino) ``` **Flujo de datos:** 1. **MetaMask** se conecta al nodo Geth via HTTPS (puerto 8443, movil) o HTTP (puerto 8545, escritorio) 2. El **Bridge API** (puerto 3001) se comunica con Geth mediante ethers.js y mantiene una base de datos SQLite con las wallets de los jugadores 3. El **mainframe CC:Tweaked** (Computer 7) se comunica con el Bridge API via HTTP dentro de la red Docker interna 4. El **mainframe** se comunica con los juegos del casino (slots, blackjack, poker, etc.) via **rednet** (protocolo de red inalambrica de CC:Tweaked) --- ## Componentes ### 1. Nodo Geth (blockchain/) El nodo Geth ejecuta la blockchain privada donde vive el token AfterCoin. | Parametro | Valor | |---|---| | Consenso | Clique PoA (Proof of Authority) | | Chain ID | 8888 | | Tiempo de bloque | 5 segundos | | Version de Geth | v1.13.15 (ultima version con soporte para Clique PoA) | | Limite de memoria | 1 GB | | Puerto HTTP RPC | 8545 | | Puerto WebSocket | 8546 | **Archivos clave:** - `genesis.json` -- Configuracion genesis de la cadena (Clique PoA, chain ID 8888, periodo de bloque de 5s) - `init-geth.sh` -- Script de inicializacion que importa la cuenta admin y arranca Geth - `Dockerfile` -- Imagen basada en `ethereum/client-go:v1.13.15` **Wallet de administrador:** Actua como sellador (sealer) de bloques y propietario (owner) del contrato inteligente. Es la unica autoridad en la cadena PoA. ### 2. Contrato AfterCoin (blockchain/contracts/AfterCoin.sol) Contrato inteligente ERC-20 autocontenido (sin dependencias de OpenZeppelin). **Caracteristicas principales:** - `decimals()` retorna `0` -- los tokens son enteros, 1 token = 1 diamante - Funciones restringidas al owner (wallet admin): - `mint(address to, uint256 amount)` -- Crea nuevos tokens y los asigna a una direccion - `burnFrom(address from, uint256 amount)` -- Quema tokens de una direccion especifica - `bridgeTransfer(address from, address to, uint256 amount)` -- Transfiere tokens entre wallets via el bridge - Cumple con el estandar ERC-20 completo (transfer, approve, transferFrom, allowance, etc.) **Compilacion:** - Compilado con `solcjs` version 0.8.34 - **Target: Paris EVM** -- No usa el opcode `PUSH0` (introducido en Shanghai EVM), ya que Geth v1.13 no lo soporta en cadenas privadas - El contrato se despliega automaticamente por el Bridge API en el primer arranque ### 3. Bridge API (services/afc-bridge/) Servicio backend que actua como puente entre el mundo de Minecraft y la blockchain. **Stack tecnologico:** Node.js + Express + ethers.js v6 + better-sqlite3 **Funcionalidades clave:** - **Auto-despliegue del contrato:** En el primer arranque, si no existe un contrato desplegado, lo despliega automaticamente y guarda la direccion en la base de datos SQLite - **Cola de nonces:** Sistema de cola para transacciones que previene colisiones cuando multiples operaciones ocurren simultaneamente **Endpoints:** | Metodo | Ruta | Descripcion | Autenticacion | |---|---|---|---| | POST | `/api/register` | Crea una wallet custodial para un jugador | `x-bridge-secret` | | POST | `/api/deposit` | Acuna (mint) AFC a la wallet del jugador | `x-bridge-secret` | | POST | `/api/withdraw` | Quema (burn) AFC de la wallet del jugador | `x-bridge-secret` | | GET | `/api/balance/:diskId` | Lee el saldo on-chain del jugador | Ninguna | | GET | `/api/wallet/:diskId` | Retorna direccion de wallet + clave privada para importar en MetaMask | Ninguna | **Seguridad:** - Los endpoints POST requieren el header `x-bridge-secret` con el secreto configurado en las variables de entorno - Los endpoints GET son publicos para facilitar la consulta de saldos y datos de wallet **Archivo estatico:** - Sirve el icono del token en `/afc-icon.svg` para que MetaMask pueda mostrar el logo del token ### 4. Proxy SSL para RPC (docker/nginx/rpc-ssl.conf) Proxy inverso Nginx que proporciona acceso HTTPS al nodo Geth. | Parametro | Valor | |---|---| | Puerto externo | 8443 (HTTPS) | | Puerto interno | 8545 (HTTP hacia Geth) | | Certificado | Let's Encrypt via Cloudflare DNS challenge | **Por que es necesario:** MetaMask en dispositivos moviles **rechaza conexiones HTTP** para endpoints RPC. El proxy SSL permite que los jugadores conecten sus wallets desde el telefono usando HTTPS. ### 5. Mainframe Lua (Computer 7) El mainframe es el computador central del casino dentro de Minecraft (CC:Tweaked). Coordina todos los juegos y gestiona los saldos de los jugadores. **Funciones principales:** | Funcion | Descripcion | |---|---| | `addPlayer(diskId, name)` | Registra una nueva wallet en el bridge para el jugador | | `getPlayerBalance(diskId)` | Sincroniza el saldo desde la blockchain (detecta transferencias hechas desde MetaMask) | | `setPlayerBalance(diskId, amount)` | Calcula la diferencia con el saldo actual y ejecuta mint o burn segun corresponda | **Caracteristicas tecnicas:** - Helpers HTTP con `pcall` como fallback para manejar errores de red - **Bucle de sincronizacion periodica** cada 30 segundos usando `parallel.waitForAll` - Los juegos del casino (slots, blackjack, poker) **no necesitan modificaciones** -- se comunican con el mainframe via rednet y este se encarga de toda la logica blockchain ### 6. Generador de Tarjetas Lua (Computer 4) Computador auxiliar que genera las tarjetas de jugador del casino. Despues de crear una tarjeta, muestra en pantalla: - La **direccion de wallet** del jugador - Instrucciones para **conectar MetaMask** y ver el saldo de AFC --- ## Servicios Docker | Servicio | Puertos | depends_on | Volumenes | Limite de Memoria | |---|---|---|---|---| | `geth` | 8545:8545, 8546:8546 | -- | `geth_data:/root/.ethereum` | 1 GB | | `afc-bridge` | 3001:3001 | `geth` | `afc_bridge_data:/app/data` | -- | | `rpc-ssl` | 8443:8443 | `geth` | `certbot_etc:/etc/letsencrypt:ro` | -- | Todos los servicios forman parte de la red Docker compartida con el servidor de Minecraft, permitiendo comunicacion interna por nombre de servicio (ej: `http://afc-bridge:3001`). --- ## Variables de Entorno Todas las variables relacionadas con AfterCoin usan el prefijo `AFC_` y se definen en el archivo `.env` del directorio `docker/`. | Variable | Descripcion | Ejemplo | |---|---|---| | `AFC_ADMIN_PRIVATE_KEY` | Clave privada de la wallet administradora (sellador + owner del contrato) | `0xabc123...` | | `AFC_ADMIN_ADDRESS` | Direccion publica de la wallet administradora | `0x742d35Cc...` | | `AFC_BRIDGE_SECRET` | Secreto compartido entre el mainframe y el Bridge API | `mi-secreto-seguro` | | `AFC_CHAIN_ID` | ID de la cadena (debe coincidir con genesis.json) | `8888` | | `AFC_RPC_URL` | URL interna del nodo Geth (dentro de Docker) | `http://geth:8545` | | `AFC_CONTRACT_ADDRESS` | Direccion del contrato desplegado (se genera automaticamente en el primer arranque) | `0x5458...918C` | --- ## Guia de Conexion con MetaMask ### Escritorio (Extension) 1. Abrir MetaMask en el navegador 2. Ir a **Settings** (Configuracion) > **Networks** (Redes) > **Add Network** (Agregar red) 3. Rellenar los campos: | Campo | Valor | |---|---| | Network Name | `AfterLife` | | RPC URL | `http://play.consultoria-as.com:8545` | | Chain ID | `8888` | | Currency Symbol | `ETH` | 4. MetaMask mostrara una advertencia sobre el chain ID desconocido -- esto es **normal para cadenas privadas**, proceder de todas formas 5. **Importar la wallet del juego:** - Menu de cuentas > **Import Account** (Importar cuenta) - Pegar la clave privada obtenida del endpoint `/api/wallet/:diskId` o de la terminal del generador de tarjetas (Computer 4) 6. **Agregar el token AFC:** - Click en **Import Tokens** (Importar tokens) > **Custom Token** (Token personalizado) - Pegar la direccion del contrato: `0x54583A08C29556d16BA626cbA66101816D79918C` - Simbolo: `AFC` - Decimales: `0` ### Movil (App) 1. Abrir la app de MetaMask 2. Ir al **menu hamburguesa** > **Settings** > **Networks** > **Add Network** 3. Rellenar los campos: | Campo | Valor | |---|---| | Network Name | `AfterLife` | | RPC URL | `https://play.consultoria-as.com:8443` | | Chain ID | `8888` | | Currency Symbol | `ETH` | > **IMPORTANTE:** En movil se DEBE usar la URL HTTPS (puerto 8443), no HTTP. MetaMask movil rechaza conexiones HTTP para endpoints RPC. 4. **Importar la wallet del juego:** - Icono de cuenta > **Add account or hardware wallet** > **Import account** - Pegar la clave privada 5. **Agregar el token AFC:** - En la pantalla principal, hacer scroll hacia abajo - **Import Tokens** > **Custom Token** - Pegar la direccion del contrato: `0x54583A08C29556d16BA626cbA66101816D79918C` ### Vincular una Wallet Personal Por defecto, el bridge crea **wallets custodiales** para cada jugador (el bridge genera y almacena las claves privadas). Si un jugador quiere vincular su propia wallet de MetaMask: 1. Actualizar la direccion en la base de datos del bridge: ```bash docker exec docker-afc-bridge-1 node -e " const db = require('./src/db'); db.db.prepare('UPDATE wallets SET address = ? WHERE disk_id = ?').run('0xDIRECCION_DEL_JUGADOR', 'DISK_ID'); " ``` 2. Acunar (mint) el saldo actual del jugador a la nueva direccion para sincronizar: ```bash curl -s localhost:3001/api/deposit \ -H "Content-Type: application/json" \ -H "x-bridge-secret: TU_SECRETO" \ -d '{"diskId":"DISK_ID","amount":SALDO_ACTUAL}' ``` > **Nota:** Al vincular una wallet personal, el jugador tendra control total sobre sus tokens y podra transferirlos libremente. Esto puede tener implicaciones en la economia del casino. --- ## Administracion ### Comandos de Verificacion ```bash # Verificar el chain ID (debe retornar 0x22b8 = 8888) curl -s -X POST localhost:8545 \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' # Registrar un jugador de prueba curl -s localhost:3001/api/register \ -H "Content-Type: application/json" \ -H "x-bridge-secret: SECRET" \ -d '{"diskId":"99","name":"Test"}' # Depositar 50 AFC (= 50 diamantes) al jugador curl -s localhost:3001/api/deposit \ -H "Content-Type: application/json" \ -H "x-bridge-secret: SECRET" \ -d '{"diskId":"99","amount":50}' # Consultar saldo de un jugador curl -s localhost:3001/api/balance/99 # Obtener informacion de wallet (direccion + clave privada) curl -s localhost:3001/api/wallet/99 # Listar todas las wallets registradas docker exec docker-afc-bridge-1 node -e \ "const db=require('./src/db');db.getAllWallets().forEach(w=>console.log(w.disk_id,w.name,w.address))" ``` ### Operaciones Directas con Tokens Para operaciones que requieren interaccion directa con el contrato inteligente (sin pasar por el Bridge API): ```bash # Acunar tokens directamente a cualquier direccion cd /tmp && node -e " const {ethers}=require('ethers'); const artifact=require('/home/AfterlifeProject/services/afc-bridge/contracts/AfterCoin.json'); async function main(){ const provider=new ethers.JsonRpcProvider('http://localhost:8545'); const admin=new ethers.Wallet('ADMIN_PRIVATE_KEY',provider); const contract=new ethers.Contract('CONTRACT_ADDRESS',artifact.abi,admin); await (await contract.mint('DIRECCION_DESTINO',CANTIDAD)).wait(); console.log('Saldo:',Number(await contract.balanceOf('DIRECCION_DESTINO'))); } main(); " ``` > **Nota:** Reemplazar `ADMIN_PRIVATE_KEY`, `CONTRACT_ADDRESS`, `DIRECCION_DESTINO` y `CANTIDAD` con los valores reales. ### Renovacion de Certificado SSL El certificado de Let's Encrypt expira cada 90 dias. Para renovarlo: ```bash # Renovar el certificado docker run --rm \ -v docker_certbot_etc:/etc/letsencrypt \ -v /tmp/certbot/cloudflare.ini:/run/secrets/cloudflare.ini:ro \ certbot/dns-cloudflare:latest renew # Reiniciar el proxy SSL para cargar el nuevo certificado docker compose -f docker-compose.dev.yml restart rpc-ssl ``` ### Whitelist HTTP de CC:Tweaked La regla de whitelist en `computercraft-server.toml` (dentro del volumen de datos de Minecraft) permite que el mainframe se comunique con el Bridge API. Si Minecraft se reinstala, la regla se pierde y debe recrearse: ```bash # Agregar la regla de whitelist para afc-bridge docker exec minecraft-ftb sed -i '/\$private/i \\t[[http.rules]]\n\t\t host = "afc-bridge"\n\t\t action = "allow"\n' /data/config/computercraft-server.toml # Reiniciar el servidor de Minecraft para aplicar cambios docker restart minecraft-ftb ``` > **IMPORTANTE:** La regla `allow` para `afc-bridge` debe aparecer **ANTES** de la regla `deny` para `$private` en el archivo de configuracion. De lo contrario, la conexion sera bloqueada por la regla de denegacion general. ### Despliegue de Scripts Lua Para actualizar los scripts de los computadores CC:Tweaked dentro de Minecraft: ```bash # Copiar el script del mainframe (Computer 7) docker cp /tmp/mainframe_startup.lua minecraft-ftb:/data/world/computercraft/computer/7/startup.lua # Copiar el script del generador de tarjetas (Computer 4) docker cp /tmp/cardgen_startup.lua minecraft-ftb:/data/world/computercraft/computer/4/startup.lua ``` Luego, reiniciar los computadores dentro del juego presionando **Ctrl+R** en cada terminal. --- ## Detalles del Contrato | Campo | Valor | |---|---| | Direccion | `0x54583A08C29556d16BA626cbA66101816D79918C` | | ABI | `services/afc-bridge/contracts/AfterCoin.json` | | Codigo fuente | `blockchain/contracts/AfterCoin.sol` | | Compilador | solcjs 0.8.34 | | Target EVM | Paris (sin opcode PUSH0) | | Desplegado en | Primer arranque del Bridge API | | Almacenamiento de direccion | Base de datos SQLite del Bridge API | --- ## Solucion de Problemas ### "invalid opcode: PUSH0" **Causa:** El contrato fue compilado para Shanghai EVM pero la cadena ejecuta Paris EVM. El opcode `PUSH0` fue introducido en Shanghai y no esta disponible en Geth v1.13 para cadenas privadas. **Solucion:** Recompilar el contrato con la opcion `--evm-version paris`: ```bash solcjs --bin --abi --evm-version paris AfterCoin.sol ``` ### Geth termina por OOM (Out of Memory) **Causa:** El nodo Geth supera el limite de memoria asignado (actualmente 1 GB). **Solucion:** Incrementar el limite de memoria en el docker-compose. La flag `--lightkdf` ya esta habilitada para reducir el uso de memoria durante la importacion de claves. ### Bridge no puede conectar con Geth **Causa:** El nodo Geth aun no ha terminado de inicializar cuando el bridge intenta conectarse. **Solucion:** Verificar que `depends_on` esta configurado correctamente en docker-compose. El bridge incluye logica de reintentos (`waitForGeth()`) que espera a que Geth este disponible antes de continuar. ### CC:Tweaked bloquea peticiones HTTP **Causa:** El archivo `computercraft-server.toml` no tiene la regla de whitelist para `afc-bridge`, o la regla esta ubicada despues de la regla de denegacion `$private`. **Solucion:** Verificar que la regla `allow` para `afc-bridge` existe y esta posicionada **antes** de la regla `deny` para `$private`. Ver la seccion [Whitelist HTTP de CC:Tweaked](#whitelist-http-de-cctweaked) para los comandos de correccion. ### MetaMask movil no puede conectar **Causa:** Se esta usando la URL HTTP (puerto 8545) en lugar de HTTPS (puerto 8443). MetaMask en dispositivos moviles requiere conexiones HTTPS para endpoints RPC. **Solucion:** Cambiar la URL de la red en MetaMask a `https://play.consultoria-as.com:8443`. ### Transacciones fallan con "nonce too low" **Causa:** Multiples transacciones se enviaron simultaneamente y los nonces colisionaron. **Solucion:** El Bridge API incluye una cola de nonces que deberia prevenir esto. Si ocurre, reiniciar el servicio del bridge: ```bash docker compose -f docker-compose.dev.yml restart afc-bridge ``` ### El saldo en MetaMask no coincide con el casino **Causa:** El jugador realizo una transferencia desde MetaMask que aun no ha sido sincronizada por el mainframe. **Solucion:** El mainframe ejecuta un bucle de sincronizacion cada 30 segundos. Esperar a que se complete el siguiente ciclo, o forzar la sincronizacion reiniciando el Computer 7 con Ctrl+R en el juego.