Whitepaper · 08
Marco de seguridad
Modelo de amenazas, invariantes, control de acceso, frontera de confianza de los hooks, gestión de las claves de cumplimiento, actualizaciones seguras y estado de auditoría de la implementación de referencia de A-REX Core.
La seguridad en un párrafo
Todo cambio de saldo del token de referencia pasa por un único punto (_update). El orden de evaluación es fijo y un bloqueo antirreentrada cubre toda la secuencia de reglas externas (hooks). La autoridad depende de roles y nunca de los saldos, el almacenamiento sigue ERC-7201 y la superficie es pequeña: ocho funciones externas que modifican el estado, además de las de ERC-1155. Las capacidades más sensibles (congelar, transferencia forzosa, administrar hooks, actualizar) son roles separados, pensados para multifirmas y timelocks, y cada uso genera un evento.
Invariantes
| Invariante | Quién la garantiza | Cómo se prueba |
|---|---|---|
totalSupply(id) es igual a la suma de los saldos de id | ERC1155Supply de OpenZeppelin; transform quema y luego acuña a través de _update | pruebas de linaje (dividir, fusionar, procesar) |
status == 255 es un estado final | setBatchStatus revierte con ARexInvalidStatusTransition | pruebas de estado |
| Un lote retirado no se puede acuñar ni transferir, solo quemar | _update | prueba de lote retirado |
amount ≤ balance − frozen cuando el movimiento lo inicia el titular | _update; forcedTransfer baja frozen a balance − amount | pruebas de congelamiento y de transferencia forzosa |
| Ningún hook puede volver a entrar en una función que modifica saldos | un bloqueo en el almacenamiento se activa antes de las verificaciones y se libera después de las notificaciones | prueba con ReentrantHook |
| Los hooks se evalúan en el orden en que se registraron; quitar uno no altera el orden | desplazamiento del arreglo en removeHook | prueba de orden |
| Una operación rechazada no cambia ningún saldo ni emite transferencias | las verificaciones corren antes de super._update | prueba de rechazo (el contador de ejecuciones no cambia) |
| Tener saldo nunca da autoridad | cada función que modifica el estado exige un rol o una aprobación de ERC-1155 | pruebas de «tener tokens no habilita nada» |
canTransfer == (transferCheck == 0) | canTransfer se define exactamente así | pruebas de conformidad |
Modelo de amenazas
Actores y capacidades
| Actor | Puede | No puede |
|---|---|---|
| Titular | transferir unidades no congeladas a cuentas habilitadas; quemar; reagrupar en lotes del mismo producto y unidad sin alterar la cantidad total | cambiar el estado, acuñar, procesar hacia otro producto, mover unidades congeladas |
| Operador aprobado | lo mismo que el titular, sobre la cuenta que lo aprobó | nada que el titular no pueda hacer |
ISSUER_ROLE | crear lotes | mover tokens de otras cuentas |
TRANSFORMER_ROLE | procesar tokens que tiene o para los que tiene aprobación | transformar tokens sin aprobación |
STATUS_ROLE | cambiar el estado (incluido retirar el lote) y actualizar los metadatos | mover tokens |
ENFORCER_ROLE | congelar cualquier cantidad; forzar cualquier transferencia | saltarse los hooks (se ejecutan con OP_FORCED_TRANSFER) |
HOOK_ADMIN_ROLE | agregar y quitar hooks | cambiar saldos directamente |
PAUSER_ROLE | detener todo, salvo las transferencias forzosas | — |
UPGRADER_ROLE | reemplazar la implementación | — (es control total; consulta la gestión de claves) |
| Contrato de hook | rechazar operaciones; observarlas; responder si una cuenta está habilitada | volver a entrar; cambiar saldos |
Escenarios
Hook malicioso o con errores (denegación de servicio). Un hook que siempre rechaza bloquea todas las transferencias del token, y uno que revierte en check tiene el mismo efecto. Mitigación: HOOK_ADMIN_ROLE puede quitar cualquier hook en cualquier momento sin alterar el orden de los demás. PAUSER_ROLE puede detener el token mientras se investiga. Las transferencias forzosas siguen funcionando, así que no se pierden los controles de cumplimiento. Al registrar un hook, el token verifica que soporte ERC-165 y que sus flags no sean cero, y rechaza las EOA. Recomendación: audita los hooks antes de registrarlos, mantén pocos y pon al administrador detrás de un timelock para que cualquier cambio sea visible antes de entrar en vigor.
Reentrada desde un hook. El onExecuted de un hook vuelve a llamar a safeTransferFrom. Mitigación: el bloqueo hace que la llamada revierta con ARexReentrantCall. Ten en cuenta que el callback de recepción de ERC-1155 (onERC1155Received) corre después de liberar el bloqueo, como en cualquier token ERC-1155. Para ese momento, el estado del token ya es definitivo.
Clave de cumplimiento comprometida. Quien tenga ENFORCER_ROLE puede mover cualquier token. Mitigación: una multifirma con un procedimiento documentado (orden judicial, contrato de prenda, aviso de sanciones). Cada acción emite ForcedTransfer o Frozen, y los hooks pueden exigir una referencia a la orden en los datos de la transferencia, porque OP_FORCED_TRANSFER no está exenta de hooks. Para medidas de cumplimiento que no sean urgentes, considera un timelock.
Clave de actualización comprometida. El control es total. Mitigación: multifirma con timelock, validaciones de actualización de OpenZeppelin en CI y almacenamiento ERC-7201 para evitar errores de layout.
Abuso del estado. STATUS_ROLE puede retirar un lote que está en manos de otros. Es intencional: un almacén, un inspector o un regulador puede declarar que un lote no es apto. Los titulares deben saberlo. Mitigación: asigna este rol a una institución que rinda cuentas; cada cambio queda como evento con su motivo.
Fraude del emisor. ISSUER_ROLE puede crear un lote que no existe físicamente. El token no lo puede impedir, pero sí deja claro quién lo hizo (issuer, originRef, metadataHash). Además, con AttestationHook puedes exigir atestaciones (certificados o constancias verificables) de terceros independientes, como un almacén, un inspector o un registro, antes de cualquier transferencia.
Sustitución de metadatos. setBatchMetadata cambia el hash y la URI. Mitigación: la función exige un rol, y los eventos BatchMetadataUpdated y URI conservan el historial. Los documentos anteriores se pueden seguir verificando contra los hashes anteriores.
Front-running de transferCheck. Una verificación previa puede quedar desactualizada antes de que se ejecute la transacción, por ejemplo si cambia el estado de un hook. La transacción vuelve a evaluar todo; en el peor caso, revierte con un código preciso.
Gas griefing mediante hooks. Cada hook agrega una llamada externa por cada id afectado y por cada fase. Un hook con bucles sin límite puede encarecer tanto las transferencias que nadie pueda pagarlas. Mitigación: reglas para escribir hooks (check con costo acotado) y revisión antes de registrarlos.
Confianza en hooks actualizables. Un hook desplegado detrás de un proxy puede cambiar la política de transferencias sin que cambie nada en el token. Mitigación: protege las claves de administración de los hooks con el mismo cuidado que HOOK_ADMIN_ROLE y, en producción, prefiere hooks inmutables.
Modelo de control de acceso
El token usa AccessControl de OpenZeppelin con ocho roles, y el registro de atestaciones tiene uno más. DEFAULT_ADMIN_ROLE administra todos. Esta es la distribución recomendada para producción:
| Rol | Quién lo tiene |
|---|---|
DEFAULT_ADMIN_ROLE, UPGRADER_ROLE, HOOK_ADMIN_ROLE | multifirma de gobernanza detrás de un timelock |
ENFORCER_ROLE | multifirma de cumplimiento (áreas de cumplimiento y legal) |
PAUSER_ROLE | clave de respuesta a incidentes (de acción rápida) |
ISSUER_ROLE, STATUS_ROLE, TRANSFORMER_ROLE | servicios operativos con privilegios mínimos y claves rotadas |
ATTEST_ADMIN_ROLE | entidad certificadora o plataforma |
Por comodidad, initialize otorga todos los roles a un solo administrador. La checklist de despliegue exige redistribuirlos antes de pasar a producción.
Controles de cumplimiento y pausa
- La pausa bloquea acuñaciones, transferencias, quemas y transformaciones. No bloquea las transferencias forzosas, de modo que una orden legal se puede ejecutar aunque haya un incidente en curso.
- Las transferencias forzosas omiten
canSend/canReceivey la verificación del saldo congelado, y bajan el monto congelado a lo que queda. Aun así, ejecutan los hooks conOP_FORCED_TRANSFER. Los hooks de referencia devuelvenOKpara esa operación; un hook que regule los controles de cumplimiento puede exigir referencias a la orden endata. - El monto congelado puede superar el saldo (ERC-7943), así que un gravamen puede cubrir depósitos futuros.
Actualizaciones seguras
- UUPS;
_authorizeUpgradeexigeUPGRADER_ROLE. - Struct de almacenamiento con espacio de nombres ERC-7201; los campos nuevos solo se agregan al final.
- Las validaciones del plugin de actualizaciones de OpenZeppelin corren en las pruebas y deben correr en CI antes de cualquier transacción de actualización.
- El ajuste
optimizer.runs = 1por archivo mantiene la implementación dentro del límite de EIP-170. Si un cambio lo supera, la compilación emite una advertencia y el despliegue revierte.
Seguridad de dependencias y compilación
- OpenZeppelin Contracts y Contracts-Upgradeable fijados en la versión 5.3.0; el lockfile está en el repositorio y CI usa
npm ci. - Solidity 0.8.22, viaIR, EVM
paris: no depende del almacenamiento transitorio y se puede desplegar en cadenas que aún no adoptaron Cancun. - No hay bibliotecas externas enlazadas ni delegatecall fuera del proxy UUPS.
Estado de la auditoría
- 46 pruebas sobre la implementación de referencia, los hooks y las extensiones cubren todas las invariantes anteriores, incluida una ejecución aleatoria de invariantes con semilla fija.
- Scripts del repositorio miden el gas y el tamaño de los contratos.
- Una auditoría externa de
ARexToken,ARexAttestationsy los hooks de referencia es requisito declarado para un despliegue ancla en producción y para presentar el ERC (consulta Desarrollos futuros). - La verificación formal de las invariantes de la cadena de reglas está en la hoja de ruta. Las invariantes son lo bastante acotadas como para expresarlas como propiedades.
Riesgos residuales que el estándar no elimina
- La existencia física y la calidad de un lote se atestan, no se demuestran. El token hace que cada afirmación tenga un responsable, no que sea verdadera.
- La disponibilidad de los metadatos fuera de la cadena depende del almacenamiento elegido. El hash garantiza la integridad, no la disponibilidad.
- La gobernanza de hooks y módulos es tan sólida como las claves que la controlan.