Como determinar o endereço de um contrato inteligente antes da implantação: usando CREATE2 para a troca de criptografia

O tópico blockchain não deixa de ser uma fonte não apenas de todos os tipos de hype, mas também de idéias que são muito valiosas do ponto de vista tecnológico. Portanto, ela não ignorou os habitantes da cidade ensolarada. As pessoas estão de olho, estudando, tentando mudar seus conhecimentos na análise informacional tradicional para sistemas de blockchain. Até agora, apenas um ponto: um dos desenvolvimentos da Rostelecom-Solar é capaz de verificar a segurança do software baseado no blockchain. E ao longo do caminho, alguns pensamentos surgem sobre a solução dos problemas aplicados da comunidade blockchain. Um desses hacks de vida - como determinar o endereço de um contrato inteligente antes da implantação usando o CREATE2 - hoje quero compartilhar com você em detalhes.

imagem
O opcode CREATE2 foi adicionado ao hard fork de Constantinopla em 28 de fevereiro deste ano. Conforme indicado no EIP, esse código de operação foi introduzido principalmente para canais de estado. No entanto, nós o usamos para resolver outro problema.

Existem usuários na bolsa com saldos. Devemos fornecer a cada usuário um endereço Ethereum para o qual qualquer pessoa pode enviar tokens, reabastecendo sua conta. Vamos chamar esses endereços de "carteiras". Quando os tokens chegam às carteiras, devemos enviá-los para uma única carteira (hotwallet).

Nas seções a seguir, analiso soluções para esse problema sem CREATE2 e explico por que as abandonamos. Se você está interessado apenas no resultado final, pode encontrá-lo na seção "Solução final".

Endereços Ethereum


A solução mais fácil é gerar novos endereços ethereum para novos usuários. Esses endereços serão carteiras. Para transferir tokens de uma carteira para uma hotwallet, você deve assinar a transação chamando a função transfer () com a chave privada da carteira do back-end.

Essa abordagem tem as seguintes vantagens:

  • é só
  • o custo de transferência de tokens da carteira para a hotwallet é igual ao preço da chamada da função transfer ()

No entanto, abandonamos essa abordagem porque ela tem uma desvantagem significativa: você precisa armazenar chaves privadas em algum lugar. E o ponto não é apenas que elas possam ser perdidas, mas também que você precisa controlar cuidadosamente o acesso a essas chaves. Se pelo menos um deles estiver comprometido, os tokens de um determinado usuário não atingirão uma carteira ativa.

imagem

Crie um contrato inteligente separado para cada usuário


A implantação de um contrato inteligente separado para cada usuário elimina a necessidade de armazenar chaves de carteira privadas no servidor. A troca chamará esse contrato inteligente de transferência de tokens para o hotwallet.

Também recusamos essa decisão, pois o usuário não pode mostrar o endereço de sua carteira sem implantar um contrato inteligente (isso é realmente possível, mas de uma maneira bastante complicada com outras deficiências, que não discutiremos aqui). Na troca, um usuário pode criar quantas contas precisar e todos precisam de sua própria carteira. Isso significa que precisamos gastar dinheiro na implantação do contrato, nem mesmo tendo certeza de que o usuário usará essa conta.

Opcode CREATE2


Para corrigir o problema do método anterior, decidimos usar o opcode CREATE2. CREATE2 permite determinar previamente o endereço no qual o contrato inteligente será implantado. O endereço é calculado usando a seguinte fórmula:

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

onde:
  • endereço - o endereço do contrato inteligente que chamará CREATE2
  • salt - valor aleatório
  • init_code - bytecode de contrato inteligente para implantação

Isso garante que o endereço que fornecemos ao usuário realmente contenha o código de código desejado. Além disso, esse contrato inteligente pode ser implantado quando necessário. Por exemplo, quando um usuário decide usar sua carteira pela primeira vez.
imagem
Além disso, você pode calcular o endereço de um contrato inteligente toda vez em vez de armazená-lo, pois:

  • O endereço na fórmula é constante, pois é o endereço da nossa fábrica de carteiras
  • salt - hash do user_id
  • O init_code é persistente, pois usamos a mesma carteira

Mais melhorias


A solução anterior ainda tem uma desvantagem: você precisa pagar para implantar um contrato inteligente. No entanto, você pode se livrar disso. Para fazer isso, você pode chamar a função transfer () e, em seguida, autodestruir () no construtor de carteira. E então o gás para a implantação do contrato inteligente será devolvido.

Ao contrário da crença popular, você pode implantar um contrato inteligente no mesmo endereço várias vezes com o opcode CREATE2. Isso ocorre porque CREATE2 verifica se o nonce do endereço de destino é zero (é atribuído o valor "1" no início do construtor). Ao mesmo tempo, a função selfdestruct () redefine os endereços nonce a cada vez. Portanto, se você chamar CREATE2 novamente com os mesmos argumentos, a verificação de nonce será aprovada.

Observe que esta solução é semelhante à opção de endereço ethereum, mas sem a necessidade de armazenar chaves privadas. O custo da transferência de dinheiro de uma carteira para a hotwallet é aproximadamente igual ao custo da chamada da função transfer () , uma vez que não pagamos pela implantação de um contrato inteligente.

Decisão final


imagem

Inicialmente preparado por:

  • função para obter sal por user_id
  • um contrato inteligente que chamará o código de operação CREATE2 com o sal apropriado (ou seja, fábrica de carteira)
  • código de byte da carteira correspondente ao contrato com o seguinte construtor:

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

Para cada novo usuário, mostramos o endereço da carteira calculando

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

Quando um usuário transfere tokens para o endereço da carteira correspondente, nosso back-end vê o evento Transfer com o parâmetro _to igual ao endereço da carteira. Neste ponto, já é possível aumentar o saldo do usuário na troca antes de implantar a carteira.

Quando um número suficiente de tokens se acumula no endereço da carteira, podemos transferi-los todos de uma vez para um hotwallet. Para fazer isso, o back-end chama a função da fábrica de contrato inteligente, que executa as seguintes ações:

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

Assim, o construtor do contrato de carteira inteligente é chamado, que transfere todos os seus tokens para o endereço da hotwallet e depois se autodestrói.

O código completo pode ser encontrado aqui . Observe que esse não é o nosso código de produção, pois decidimos otimizar o bytecode da carteira e o escrevemos nos códigos de operação.

Postado por Pavel Kondratenkov, especialista em Ethereum

Source: https://habr.com/ru/post/pt475496/


All Articles