Cómo determinar la dirección de un contrato inteligente antes de la implementación: usar CREATE2 para un intercambio de cifrado

El tema blockchain no deja de ser fuente no sólo de todo tipo de publicidad, sino también de ideas muy valiosas desde el punto de vista tecnológico. Por lo tanto, no pasó por alto a los residentes de la soleada ciudad. La gente observa de cerca, estudia e intenta transferir su experiencia en seguridad de la información tradicional a los sistemas blockchain. Hasta ahora, ha dado en el clavo: uno de los desarrollos de Rostelecom-Solar puede comprobar la seguridad del software basado en blockchain. Y en el camino, surgen algunas ideas sobre cómo resolver problemas aplicados de la comunidad blockchain. Uno de estos trucos de vida: cómo determinar la dirección de un contrato inteligente antes de implementarlo usando CREATE2, hoy quiero compartirlo con ustedes.

Cómo determinar la dirección de un contrato inteligente antes de la implementación: usar CREATE2 para un intercambio de cifrado
El código de operación CREATE2 se agregó en el hard fork de Constantinopla el 28 de febrero de este año. Como se indica en el EIP, este código de operación se introdujo principalmente para canales estatales. Sin embargo, lo usamos para resolver un problema diferente.

Hay usuarios con saldos en el intercambio. Debemos proporcionar a cada usuario una dirección de Ethereum a la que cualquiera pueda enviar tokens, recargando así su cuenta. Llamemos a estas direcciones "billeteras". Cuando los tokens llegan a las billeteras, debemos enviarlos a una sola billetera (hotwallet).

En las siguientes secciones, analizo opciones para resolver este problema sin CREATE2 y les cuento por qué las abandonamos. Si solo te interesa el resultado final, puedes encontrarlo en la sección “Solución final”.

Direcciones de Ethereum

La solución más sencilla es generar nuevas direcciones de Ethereum para nuevos usuarios. Estas direcciones serán las billeteras. Para transferir tokens de una billetera a una billetera activa, debe firmar la transacción llamando a la función transferir() con la clave privada de la billetera desde el backend.

Este enfoque tiene las siguientes ventajas:

  • es solo
  • el costo de transferir tokens de una billetera a un hotwallet es igual al costo de una llamada a función transferir()

Sin embargo, decidimos no utilizar este enfoque porque tiene un gran inconveniente: es necesario almacenar las claves privadas en algún lugar. No sólo se pueden perder, sino que también es necesario gestionar cuidadosamente el acceso a estas claves. Si al menos uno de ellos se ve comprometido, los tokens de un usuario en particular no llegarán a la billetera activa.

Cómo determinar la dirección de un contrato inteligente antes de la implementación: usar CREATE2 para un intercambio de cifrado

Cree un contrato inteligente separado para cada usuario

La implementación de un contrato inteligente separado para cada usuario le permite evitar almacenar claves privadas para billeteras en el servidor. El intercambio llamará a este contrato inteligente para transferir los tokens al hotwallet.

También abandonamos esta solución, ya que al usuario no se le puede mostrar la dirección de su billetera sin implementar un contrato inteligente (esto es realmente posible, pero de una manera bastante compleja con otras desventajas que no discutiremos aquí). En el intercambio, un usuario puede crear tantas cuentas como necesite y cada una necesita su propia billetera. Esto significa que debemos gastar dinero en implementar un contrato sin siquiera estar seguros de que el usuario utilizará esta cuenta.

Código de operación CREAR2

Para solucionar el problema del método anterior, decidimos utilizar el código de operación CREATE2. CREATE2 le permite predeterminar la dirección donde se implementará el contrato inteligente. La dirección se calcula mediante la siguiente fórmula:

keccak256 (0xff ++ address ++ salt ++ keccak256 (init_code)) [12:]


, donde:

  • dirección — la dirección del contrato inteligente que llamará a CREATE2
  • sal - valor aleatorio
  • código_inicial — código de bytes de contrato inteligente para implementación

Esto garantiza que la dirección que proporcionamos al usuario realmente contenga el código de bytes deseado. Además, este contrato inteligente se puede implementar cuando lo necesitemos. Por ejemplo, cuando un usuario decide utilizar su billetera por primera vez.
Cómo determinar la dirección de un contrato inteligente antes de la implementación: usar CREATE2 para un intercambio de cifrado
Además, puedes calcular la dirección del contrato inteligente cada vez en lugar de almacenarla porque:

  • dirección en la fórmula es constante, ya que esta es la dirección de nuestra fábrica de billeteras
  • sal - hash de ID de usuario
  • código_inicial es constante ya que usamos la misma billetera

Más mejoras

La solución anterior todavía tiene un inconveniente: hay que pagar para implementar el contrato inteligente. Sin embargo, puedes deshacerte de él. Para hacer esto puedes llamar a la función. transferir()y luego auto destrucción() en el constructor de billetera. Y luego se devolverá el gas para implementar el contrato inteligente.

Contrariamente a la creencia popular, puedes implementar un contrato inteligente en la misma dirección varias veces con el código de operación CREATE2. Esto se debe a que CREATE2 verifica que el nonce de la dirección de destino sea cero (se le asigna el valor "1" al comienzo del constructor). En este caso, la función auto destrucción() restablece las direcciones nonce cada vez. Entonces, si vuelves a llamar a CREATE2 con los mismos argumentos, la verificación nonce pasará.

Tenga en cuenta que esta solución es similar a la opción de dirección de Ethereum, pero sin la necesidad de almacenar claves privadas. El costo de transferir dinero de una billetera a un hotwallet es aproximadamente igual al costo de llamar a una función transferir(), ya que no pagamos por la implementación de contratos inteligentes.

Decisión definitiva

Cómo determinar la dirección de un contrato inteligente antes de la implementación: usar CREATE2 para un intercambio de cifrado

Preparado originalmente por:

  • función para obtener sal por user_id
  • un contrato inteligente que llamará al código de operación CREATE2 con la sal adecuada (es decir, fábrica de billeteras)
  • Código de bytes de billetera correspondiente al contrato con el siguiente constructor:

constructor () {
    address hotWallet = 0x…;
    address token = 0x…;
    token.transfer (hotWallet, token.balanceOf (address (this)));
    selfdestruct (address (0));
}


Para cada nuevo usuario mostramos la dirección de su billetera mediante cálculo

keccak256 (0xff ++ address ++ salt ++ keccak256 (init_code)) [12:]


Cuando un usuario transfiere tokens a la dirección de billetera correspondiente, nuestro backend ve un evento de Transferencia con el parámetro _para, igual a la dirección de la billetera. En este punto, ya es posible aumentar el saldo del usuario en el intercambio antes de implementar la billetera.

Cuando una dirección de billetera acumula una cantidad suficiente de tokens, podemos transferirlos todos a la vez a hotwallet. Para hacer esto, el backend llama a la función de fábrica de contratos inteligentes, que realiza las siguientes acciones:

function deployWallet (соль uint256) {
    bytes memory walletBytecode =…;
    // invoke CREATE2 with wallet bytecode and salt
}


Por lo tanto, se llama al constructor de contratos inteligentes de billetera, que transfiere todos sus tokens a la dirección de la billetera activa y luego se autodestruye.

El código completo se puede encontrar aquí. Tenga en cuenta que este no es nuestro código de producción, ya que decidimos optimizar el código de bytes de la billetera y lo escribimos en códigos de operación.

Autor Pavel Kondratenkov, especialista en Ethereum

Fuente: habr.com

Añadir un comentario