Texto original: "Direcciones personalizadas" de foobar
Recopilación: Gachas nocturnas, el camino de DeFi
160 millones de dólares estadounidenses desaparecieron y Wintermute perdió los fondos. Wintermute es uno de los fondos de creación de mercado más astutos de la industria. Una mañana de septiembre, cuando el responsable de Wintermute se despertó, descubrió que una de sus billeteras importantes había perdido 9 cifras. Entonces, ¿qué llevó al robo de Wintermute? Esto se debe a una mala aleatoriedad en un generador de direcciones personalizadas. Los hackers de sombrero negro fuerzan bruscamente el par de clave privada y dirección pública desde cero, y luego se transfiere una gran cantidad de criptoactivos.
También hay una historia sobre el pirateo de Indexed Finance, donde les robaron $ 16 millones en octubre de 2021, y los fondos robados luego se trasladaron a direcciones que comenzaban con 0xba5ed…. Lo que no sabían era que la dirección personalizada también se vio afectada por la vulnerabilidad de aleatoriedad que afectó a Wintermute, y en septiembre de 2022, todo el dinero fue robado nuevamente y fue a otra dirección de billetera pirateada. Los ladrones son despiadados.
Entonces, ¿qué problemas encontraron estos talentosos desarrolladores y qué podemos aprender de ellos?
A la izquierda está la dirección normal utilizada para el contrato WETH. A la derecha hay una bonita dirección con 14 ceros a la izquierda para la optimización del robot MEV. El tipo más común de dirección personalizada es aquella con muchos ceros a la izquierda.
En primer lugar, ¿qué es una dirección personalizada (también conocida como dirección personalizada)? Una dirección personalizada se refiere a una dirección pública que un usuario crea deliberadamente para asociar con su billetera o contrato inteligente. Tal vez comience con 0x0000000, tal vez comience con 0xdeadbeef, tal vez sea alguna otra dirección normal. Hay varias razones para su popularidad:
1. Optimización del gas: Wintermute ahorró $15 000 al utilizar una dirección EOA con varios ceros a la izquierda. ¿Suena tonto? Muchas personas están de acuerdo, ¡pero así es como funciona el EVM! Si tiene muchos ceros en su dirección, la tarifa de transacción de gas puede bajar. Entonces, si usa una dirección de contrato inteligente con muchos ceros a la izquierda, los usuarios estarán felices cuando interactúen con ella porque les ahorra dinero.
El Libro Amarillo de Ethereum describe cómo las direcciones cero líderes pueden permitir gas más barato
2. Marca del acuerdo. ¿Sabes que el contrato Token de 1 pulgada comienza con 0x111111111...?
Contrato de token de 1 pulgada
3. Repetibilidad multihebra. En mi opinión, esta es una máxima prioridad y la razón por la que cada protocolo debería utilizar una dirección personalizada para su implementación. ¡Su aplicación puede vivir en 15 cadenas EVM diferentes y tener la misma dirección en todas partes! ¿No sería esto más fácil para los desarrolladores y usuarios?
Entonces, ¿cuándo es segura una dirección bonita?
Hay dos tipos de direcciones Ethereum: cuentas de propiedad externa (EOA) y cuentas de contrato inteligente. Si ha utilizado una billetera como MetaMask, cada dirección que contiene es una EOA, que se utiliza para firmar mensajes y procesar transacciones. Compare esto con una cuenta de contrato inteligente como el contrato Uniswap, con el que las personas pueden interactuar pero no puede realizar sus propias acciones sin que se active. En resumen, es muy simple: las direcciones con buenos números no son seguras para las cuentas EOA, pero sí lo son para las cuentas de contratos inteligentes.
Entonces, ¿por qué es así? Explicaremos esto con más detalle a continuación, pero depende de cómo se genera la dirección personalizada. Para las cuentas EOA, recorre millones de claves privadas hasta encontrar una que corresponda a una dirección pública atractiva. Sin embargo, la clave privada controla los fondos dentro de una cuenta EOA, por lo que si la aleatoriedad que utiliza para atravesar la clave privada se ve comprometida, toda su cuenta se arruina. Por otro lado, crear direcciones personalizadas de contratos inteligentes solo requiere atravesar semillas públicas, que no otorgan ningún derecho administrativo al contrato inteligente.
Esta es la razón por la que Wintermute falla y OpenSea tiene éxito: generar claves privadas en una memoria insegura con software inseguro es malo. ¡Pero generar semillas públicas de esta manera es bastante bueno! Por lo tanto, una buena dirección de EOA es un camino hacia la quiebra, mientras que una buena dirección de contrato inteligente es un camino hacia el éxito.
¿Por qué el protocolo requiere una dirección bonita?
¡Documentación más sencilla! Puede señalar una dirección de contrato en todas las cadenas;
Usuario verificable! La misma dirección de contrato ocurrirá solo si y solo si el código de bytes coincide byte por byte;
¡Los desarrolladores pueden verificarlo! Dado que las direcciones de contrato idénticas sólo ocurren en el caso de una coincidencia exacta, puede detectar pequeñas modificaciones complicadas en su script de implementación;
¡Integración más fácil! Otros protocolos pueden codificar la dirección de su contrato en su código de cadena múltiple sin tener que usar declaraciones if basadas en chainId.
NOTA: Estamos a punto de profundizar en un manual de instrucciones detallado. Al juntar todas las piezas por primera vez, nos adentramos profundamente en el espacio técnico y nos dirigimos a desarrolladores de contratos inteligentes con experiencia en la implementación de contratos inteligentes en cadena. Si estás interesado, sigue leyendo, pero si no es para ti, ¡no te preocupes por seguir el ritmo! Hay un desafío técnico adicional al final (con recompensas).
Hermosa dirección de contrato inteligente
Hay una manera de generar direcciones personalizadas de contratos inteligentes que son 100% seguras, sin importar qué software use, no importa si la tecnología iterativa se filtra públicamente. Se llama "Método de fábrica CREATE2" y no solo proporciona una dirección bonita, sino que también es una forma infalible de garantizar que tiene la misma dirección de implementación del contrato en múltiples cadenas. También permite que otros implementen código sin confianza en su nombre sin compartir claves privadas ni suposiciones.
Primero, una descripción general rápida de cómo elegir una dirección de contrato inteligente. Hay dos opciones de implementación, CREATE y CREATE2. Cuando implementa un contrato inteligente directamente desde EOA, el proceso predeterminado es CREAR. La dirección se determina combinando la dirección del creador del contrato con el nonce del creador del contrato. Este nonce se refiere a cuántas transacciones ha enviado una dirección, por lo que una nueva billetera comienza en 0 y se incrementa en 1 cada vez que se envía una nueva transacción. Aquí está la fórmula mágica para la dirección de contrato inteligente implementada por CREATE:
nueva_dirección = hash(remitente, nonce)
Menos común, pero más interesante, es la dirección de contrato inteligente implementada con CREATE2, y aquí está su fórmula:
nueva_dirección = hash(0xFF, remitente, sal, código de bytes)
Lo primero parece más sencillo, ¿verdad? Sin embargo, demos un ejemplo de dónde esta simplicidad puede ser perjudicial en comparación con el proceso CREATE2, más sólido.
Airy Alice: Hay un problema con las cadenas múltiples
Imagine que una desarrolladora de criptomonedas llamada Alice crea dos contratos inteligentes: una bifurcación de Uniswap llamada GriddleSwap y un proyecto NFT llamado ph00ts. Todos son primitivos independientes inmutables, lo que significa que no hay dependencias externas ni riesgos de puentes entre cadenas. Alice implementa GriddleSwap en Ethereum usando nonce 0 y luego implementa ph00ts en Ethereum usando nonce 1. Desafortunadamente, Alice tiene poca capacidad de atención y se distrajo en Twitter criptográfico durante unos minutos antes de implementar su trabajo en Binance Smart Chain (BSC), la segunda plataforma de contratos inteligentes más grande.
¡Vaya, arruinó el orden de implementación!
¡Pero espera! Ella arruinó el orden de implementación e implementó ph00ts antes de GriddleSwap. Dado que la dirección del contrato inteligente solo depende de la dirección del creador, y en la cadena de bloques implementada, Ethereum gridleswap tiene exactamente la misma dirección que BSC ph00ts. Para empeorar las cosas, la dirección de Ethereum ph00ts es la misma que la dirección de BSC GriddleSwap. Pensar que los usuarios finales se sentirán confundidos es quedarse corto. De hecho, los implementadores maliciosos pueden abusar de él para engañar a las personas haciéndoles pensar que el comportamiento del contrato en la cadena es el mismo, lo cual es una suposición justa, dada la misma dirección.
Cuidado Alice: todavía habrá problemas.
Incluso si Alice tiene cuidado al desplegar y nunca confunde el orden de sus nonces, existen otros problemas. Si Alice se implementa correctamente en Ethereum y BSC, pero luego realiza una transacción no relacionada en Polygon, nonce 0 se habrá agotado. Nunca podrá implementar GriddleSwap allí porque su nonce se ha incrementado. Por lo tanto, las claves privadas del implementador deben protegerse a toda costa. Si Alice lo filtra, un saboteador malicioso puede realizar transacciones no relacionadas. Si Alice lo pierde, también perderá la capacidad de implementar nuevamente en esa dirección en una nueva cadena. Esta es una vulnerabilidad permanente que depende de que un individuo honesto proteja la clave privada. Si ni siquiera los desarrolladores principales de Bitcoin pueden hacerlo, ¿cómo podemos hacerlo el resto de nosotros?
Solución: CREAR2
Afortunadamente, existe una mejor manera de obtener direcciones consistentes en todas las cadenas: una que no dependa de claves privadas secretas, que no dependa de un único implementador y que sea resistente a errores del implementador en el camino. Recuerde la fórmula para encontrar la dirección de un contrato inteligente implementado con CREATE2:
nueva_dirección = hash(0xFF, remitente, sal, código de bytes)
El primer parámetro 0xFF es un valor constante que puede ignorarse. El segundo parámetro (dirección del remitente) se puede hacer coherente seleccionando la implementación CREATE2Factory de z0age 0x0000000000FFe8B47B3e2130213B802212439497 en la mayoría de las cadenas EVM. El tercer parámetro es un salt seleccionado por el usuario, que podemos usar para encontrar una buena dirección y luego mantenerla sin cambios en la cadena. El cuarto es el código de bytes del contrato, que sirve como una comprobación útil para garantizar que estamos implementando exactamente la misma funcionalidad en la cadena. Los cuatro parámetros pueden permanecer iguales sin importar lo que haga cada implementador.
¿Por qué es esto mejor? A diferencia de la clave privada, el salt seleccionado por el implementador puede hacerse público. Conocer el salt le permite implementar el contrato, pero no tiene control sobre los activos o la funcionalidad del contrato. Debido a que no vincula ninguna información secreta, cualquiera puede implementar el contrato en la nueva cadena sin revelar ni compartir la clave privada. El parámetro de código de bytes también garantiza que estas nuevas implementaciones sin permiso tendrán la misma dirección si y sólo si el código de bytes es el mismo. Por lo tanto, los usuarios finales obtienen garantías más sólidas sin tener que hacer diferencias detalladas en el código.
Para obtener una descripción más detallada, consulte el artículo de divulgación científica de OpenZeppelin.
Crea tu propia y hermosa dirección
¿Cree que la prueba de trabajo (PoW) será inútil después de la fusión de Ethereum? ¡Piensa otra vez! Las mismas capacidades de GPU que ayudan a encontrar preimágenes hash con una gran cantidad de ceros a la izquierda para bloques de Bitcoin también son excelentes para encontrar preimágenes hash con una gran cantidad de ceros a la izquierda para contratos inteligentes EVM. z0age de OpenSea (gracias a su explicación para esta publicación) encontró una configuración simple para crear su propia dirección personalizada.
1. Utilice vast.ai para lanzar una instancia de ejemplo de GPU, que intenta aproximadamente 2 mil millones de veces por segundo y cuesta alrededor de 25 centavos por hora:
Imagen: nvidia/opencl
GPU: 1x RTX 3090
Espacio en disco necesario para asignar: 1,83 GB
2. SSH e instalar Rust + create2crunch
sudo apto instalar build-essential -y; curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; fuente "$HOME/.carga/env"; git clone https://github.com/0age/create2crunch && cd create2crunch; sed -i 's/0x4/0x40/g' src/lib.rs
3. Ejecute una búsqueda de semillas. Para las variables de entorno, INIT_CODE_HASH es el keccak256 del código de creación del contrato. Puede encontrar una copia impresa de una prueba de fundición de muestra aquí. ¡Asegúrese de verificarla antes de consumir grandes cantidades de recursos informáticos! PRINCIPAL debe ser el número de bytes cero iniciales que desea y TOTAL debe ser el número total de bytes cero que desea en la dirección del contrato.
exportar FÁBRICA="0x0000000000ffe8b47b3e2130213b802212439497"; exportar LLAMADOR="0x00000000000000000000000000000000000000000"; exportar INIT_CODE_HASH="0xabc...def"; exportar LÍDER=5; exportar TOTAL=7; ejecución de carga --liberación $FACTORY $CALLER $INIT_CODE_HASH 0 $LEADING $TOTAL
Cuando z0age lanzó por primera vez su repositorio, era capaz de ejecutar 1.900 millones de intentos por segundo en el hardware vastAI antes mencionado. Desde entonces, la vectorización se ha vuelto loca en algunos núcleos OpenGL y he observado 2,150 millones de intentos por segundo. Esto significa que encontrar una dirección con 5 bytes cero iniciales tomaría 256^5/(2150000000 * 60) ~= 8 minutos y encontrar una dirección con 6 bytes cero iniciales tomaría 256^6/(2150000000 * 3600) ~ = 36 horas Una dirección de 7 bytes cero a la izquierda tarda 256^7/(2150000000 * 86400) ~= 387 días. Tenga en cuenta que un byte equivale a dos caracteres hexadecimales, por lo que una dirección de 5 bytes iniciales tendrá 10 ceros. Por supuesto, esta búsqueda puede ser totalmente paralelizada y la probabilidad real de éxito a lo largo del tiempo seguirá una distribución de Poisson.
Implementar la fábrica CREATE2
Los lectores astutos pueden haber notado que CREATE2 Factory ya existe en 0x0000000000FFe8B47B3e2130213B802212439497 en todas las cadenas. Es un problema del huevo y la gallina, ¿cómo depende la implementación consistente de direcciones de la implementación consistente de direcciones?
Cuando me enteré por primera vez de este enfoque, pensé que era simplemente una clave privada en poder de alguien más inteligente que yo (el escenario de "Alice observadora" anterior). ¡Pero en realidad es mucho más sólido que eso! El enfoque de "transacciones sin clave" del fundador de ENS, Nick Johnson, explota el hecho de que se puede recuperar la dirección pública de cualquier firma de transacción sin conocer la clave privada correspondiente que la firmó. Por lo tanto, es posible crear una transacción ("implementar una fábrica create2") y luego inventar una firma falsificada, como una que consista solo en 2. La clave privada de esta firma falsificada existe, pero nadie sabe cuál es. Pero podemos recuperar la dirección pública correspondiente a la "firma sin llave", enviarle algo de ETH y luego enviar la transacción firmada al mempool. A pesar de la oscuridad de este método, es una transacción válida y, de hecho, la única transacción válida que se puede enviar desde esta dirección pública.
El resultado: cualquiera puede implementar una fábrica en una nueva cadena sin ninguna información de propiedad exclusiva, evitando al mismo tiempo que actores malintencionados causen daños. Crear un EOA de propósito único que pueda implementar una sola transacción es una técnica muy inteligente.
Direcciones y contratos específicos creados durante transacciones sin llave
Esto se puede lograr con tres simples comandos de "forja". El código de bytes es demasiado largo para copiarlo aquí, pero puede seguir las instrucciones en https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md sin permiso en cualquier cadena de su elección. ¡Implemente CREATE2 Factory fácilmente!
Por supuesto, si ya se ha implementado, no es necesario volver a implementarlo.
Nota al margen: los requisitos de EIP-155 son terribles
Un breve intento de apoyar mis aventuras de gobernanza L1, siéntete libre de seguir adelante. EIP-155 es una propuesta presentada por Vitalik en 2016, que introduce el concepto de "identificación de cadena" para evitar ataques de repetición. Cada cadena tendrá su propio identificador único (1 para Ethereum, 56 para BSC y 137 para Polygon) que se incluirá en las transacciones firmadas para evitar ataques de repetición, que fue rápidamente adoptado por Ethereum, y todas las demás cadenas EVM han seguido esta propuesta. Esto es genial, pero el problema surge cuando se seleccionan algunas cadenas, como Evmos, que recientemente decidió prohibir explícitamente las transacciones antes de EIP-155, lo que, por extrañas razones, evita un error operativo en el que Optimism envió 20 millones de tokens OP a un Dirección multifirma (sí, ellos de nuevo) que Wintermute no existe, afirma ser propietaria pero nunca se inicializó. Sin embargo, deshabilitar las transacciones anteriores a 155 interrumpiría significativamente todo un conjunto de implementaciones entre cadenas, como la fábrica CREATE2 y proyectos líderes como Seaport. Estas propuestas de gobernanza deberían revertirse de inmediato, y detalles de protección como este deberían provenir de la billetera y no de la capa de consenso. Si las cadenas múltiples son el futuro, entonces estas restricciones innecesarias serán un gran obstáculo para que los mejores proyectos se implementen en su cadena de bloques.
Cosas divertidas: implementar recompensas
Hoy https://delegate.cash está implementado en 7 cadenas EVM diferentes (Ethereum, Polygon, Optimism, Celo, Avalanche, Fantom y Arbitrum) y 7 testnets correspondientes a estas cadenas. La dirección del contrato para todos ellos es la misma: 0x00000000000076A84feF008CDAbe6409d2FE638B.
Entonces ¿es esto suficiente? No, necesitamos más cadenas. Debido a que delegadocash es una primitiva independiente con cero dependencias, esto significa que el riesgo de múltiples cadenas es efectivamente cero. ¡Esto es puro beneficio! Por lo tanto, a las primeras 5 personas que implementen y verifiquen el contrato inteligente de delegado de efectivo en la nueva cadena y la red de prueba correspondiente, ¡les otorgaré un bono de 100 USDC!
Debe utilizar el script de implementación del repositorio de código abierto aquí. Esto puede requerir la implementación de CREATE2 Factory si no existe, ¡y no olvide la verificación de Etherscan! ¡Feliz implementación y disfrute del aprendizaje experiencial!
