Como determinar o enderezo dun contrato intelixente antes da implantación: usando CREATE2 para un intercambio criptográfico

O tema da cadea de bloques non deixa de ser fonte non só de todo tipo de bombo, senón tamén de ideas moi valiosas desde o punto de vista tecnolóxico. Polo tanto, non pasou por alto os veciños da soleada cidade. A xente está mirando de preto, estudando, intentando transferir a súa experiencia en seguridade da información tradicional aos sistemas blockchain. Ata agora, está acertado: un dos desenvolvementos de Rostelecom-Solar pode comprobar a seguridade do software baseado en blockchain. E ao longo do camiño, xorden algunhas reflexións sobre a solución de problemas aplicados da comunidade blockchain. Un destes trucos de vida - como determinar o enderezo dun contrato intelixente antes da implantación mediante CREATE2 - hoxe quero compartir contigo baixo o corte.

Como determinar o enderezo dun contrato intelixente antes da implantación: usando CREATE2 para un intercambio criptográfico
O código de operación CREATE2 engadiuse no hard fork de Constantinopla o 28 de febreiro deste ano. Como se indica no EIP, este código de operación foi introducido principalmente para canles estatais. Non obstante, utilizámolo para resolver un problema diferente.

Hai usuarios con saldos no intercambio. Debemos proporcionar a cada usuario un enderezo de Ethereum ao que calquera pode enviar tokens, repoñendo así a súa conta. Chamemos a estes enderezos "carteras". Cando as fichas chegan ás carteiras, debemos envialas a unha única carteira (hotwallet).

Nas seguintes seccións, analizo opcións para resolver este problema sen CREATE2 e cóntoche por que as abandonamos. Se só che interesa o resultado final, podes atopalo na sección "Solución final".

Enderezos de Ethereum

A solución máis sinxela é xerar novos enderezos de Ethereum para novos usuarios. Estes enderezos serán as carteiras. Para transferir tokens dunha carteira a un hotwallet, cómpre asinar a transacción chamando á función Transferir() coa clave privada da carteira do backend.

Este enfoque ten as seguintes vantaxes:

  • é xusto
  • o custo de transferencia de tokens dunha carteira a un hotwallet é igual ao custo dunha chamada de función Transferir()

Non obstante, decidimos non este enfoque porque ten un gran inconveniente: cómpre almacenar as claves privadas nalgún lugar. Non só se poden perder, senón que tamén debes xestionar coidadosamente o acceso a estas chaves. Se polo menos un deles está comprometido, os tokens dun usuario en particular non chegarán á carteira quente.

Como determinar o enderezo dun contrato intelixente antes da implantación: usando CREATE2 para un intercambio criptográfico

Crea un contrato intelixente separado para cada usuario

A implantación dun contrato intelixente separado para cada usuario permítelle evitar o almacenamento de claves privadas para as carteiras no servidor. O intercambio chamará a este contrato intelixente para transferir os tokens ao hotwallet.

Tamén abandonamos esta solución, xa que non se lle pode mostrar ao usuario o seu enderezo de carteira sen implantar un contrato intelixente (isto é realmente posible, pero dun xeito bastante complexo con outras desvantaxes que non comentaremos aquí). No intercambio, un usuario pode crear tantas contas como necesite, e cada un necesita a súa propia carteira. Isto significa que necesitamos gastar diñeiro na implantación dun contrato sen sequera estar seguro de que o usuario utilizará esta conta.

Código de operación CREATE2

Para solucionar o problema do método anterior, decidimos usar o código de operación CREATE2. CREATE2 permítelle predeterminar o enderezo onde se implementará o contrato intelixente. O enderezo calcúlase mediante a seguinte fórmula:

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


onde:

  • enderezo — o enderezo do contrato intelixente que chamará CREATE2
  • sal - valor aleatorio
  • init_code — código de bytes de contrato intelixente para a súa implantación

Isto garante que o enderezo que proporcionamos ao usuario contén realmente o bytecode desexado. Ademais, este contrato intelixente pódese implementar sempre que o necesitemos. Por exemplo, cando un usuario decide usar a súa carteira por primeira vez.
Como determinar o enderezo dun contrato intelixente antes da implantación: usando CREATE2 para un intercambio criptográfico
Ademais, pode calcular o enderezo do contrato intelixente cada vez en lugar de almacenalo porque:

  • enderezo na fórmula é constante, xa que este é o enderezo da nosa fábrica de carteiras
  • sal - user_id hash
  • init_code é constante xa que usamos a mesma carteira

Máis melloras

A solución anterior aínda ten un inconveniente: cómpre pagar para implementar o contrato intelixente. Non obstante, podes desfacerte del. Para iso podes chamar á función Transferir(), e logo autodestrución () no constructor da carteira. E entón devolverase o gas para despregar o contrato intelixente.

Ao contrario da crenza popular, podes implementar un contrato intelixente no mesmo enderezo varias veces co código de operación CREATE2. Isto débese a que CREATE2 comproba que o nonce do enderezo de destino é cero (asignaselle o valor "1" ao comezo do construtor). Neste caso, a función autodestrución () restablece os enderezos nonce cada vez. Polo tanto, se chamas de novo a CREATE2 cos mesmos argumentos, a comprobación nonce pasará.

Teña en conta que esta solución é semellante á opción de enderezo de Ethereum, pero sen necesidade de almacenar claves privadas. O custo de transferir diñeiro dunha carteira a un hotwallet é aproximadamente igual ao custo de chamar a unha función Transferir(), xa que non pagamos a implantación de contratos intelixentes.

Decisión final

Como determinar o enderezo dun contrato intelixente antes da implantación: usando CREATE2 para un intercambio criptográfico

Elaborado orixinalmente por:

  • función para sacar sal ID do usuario
  • un contrato intelixente que chamará ao código de operación CREATE2 co sal apropiado (é dicir, a fábrica de carteiras)
  • código de bytes de carteira correspondente ao contrato co seguinte construtor:

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


Para cada usuario novo mostrámoslle o seu enderezo de carteira por cálculo

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


Cando un usuario transfire tokens ao enderezo da carteira correspondente, o noso backend ve un evento de transferencia co parámetro _a, igual ao enderezo da carteira. Neste punto, xa é posible aumentar o saldo do usuario no intercambio antes de despregar a carteira.

Cando un enderezo de carteira acumula un número suficiente de tokens, podemos transferilos todos á vez ao hotwallet. Para iso, o backend chama á función de fábrica de contrato intelixente, que realiza as seguintes accións:

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


Así, chámase o constructor de contrato intelixente de carteira, que transfire todos os seus tokens ao enderezo hotwallet e despois autodestrúese.

Pódese atopar o código completo aquí. Teña en conta que este non é o noso código de produción, xa que decidimos optimizar o bytecode da carteira e escribiuno en códigos de operación.

Autor Pavel Kondratenkov, especialista en Ethereum

Fonte: www.habr.com

Engadir un comentario