Comment déterminer l'adresse d'un contrat intelligent avant le déploiement : utiliser CREATE2 pour un échange crypto

Le thème de la blockchain ne cesse d’être une source non seulement de battage médiatique de toutes sortes, mais aussi d’idées très précieuses d’un point de vue technologique. Elle n’a donc pas contourné les habitants de la ville ensoleillée. Les gens regardent de près, étudient, tentent de transférer leur expertise de la base d'informations traditionnelle vers les systèmes blockchain. Jusqu'à présent, de manière ponctuelle : l'un des développements de Rostelecom-Solar est capable de vérifier la sécurité des logiciels basés sur la blockchain. Et en cours de route, certaines réflexions surgissent sur la résolution des problèmes appliqués de la communauté blockchain. L'une de ces astuces de vie - comment déterminer l'adresse d'un contrat intelligent avant le déploiement à l'aide de CREATE2 - je souhaite aujourd'hui la partager avec vous sous la coupe.

Comment déterminer l'adresse d'un contrat intelligent avant le déploiement : utiliser CREATE2 pour un échange crypto
L'opcode CREATE2 a été ajouté dans le hard fork de Constantinople le 28 février de cette année. Comme indiqué dans l'EIP, cet opcode a été introduit principalement pour les chaînes nationales. Cependant, nous l’avons utilisé pour résoudre un problème différent.

Il y a des utilisateurs avec des soldes sur la bourse. Nous devons fournir à chaque utilisateur une adresse Ethereum, à laquelle n'importe qui peut envoyer des tokens, reconstituant ainsi son compte. Appelons ces adresses « portefeuilles ». Lorsque les tokens arrivent dans les wallets, il faut les envoyer vers un seul wallet (hotwallet).

Dans les sections suivantes, j'analyse les options pour résoudre ce problème sans CREATE2 et explique pourquoi nous les avons abandonnées. Si vous êtes uniquement intéressé par le résultat final, vous pouvez le trouver dans la section Solution finale.

Adresses Ethereum

La solution la plus simple consiste à générer de nouvelles adresses Ethereum pour les nouveaux utilisateurs. Ces adresses seront les portefeuilles. Pour transférer des jetons du portefeuille vers le hotwallet, vous devez signer la transaction en appelant la fonction transfert() avec la clé privée du portefeuille du backend.

Cette approche présente les avantages suivants :

  • c'est simple
  • le coût de transfert des jetons du portefeuille vers le hotwallet est égal au coût d'appel de la fonction transfert()

Cependant, nous avons abandonné cette approche car elle présente un inconvénient majeur : vous devez stocker les clés privées quelque part. Et ce n’est pas seulement qu’elles peuvent être perdues, mais aussi que vous devez gérer soigneusement l’accès à ces clés. Si au moins l’un d’entre eux est compromis, les jetons d’un certain utilisateur n’atteindront pas le hot wallet.

Comment déterminer l'adresse d'un contrat intelligent avant le déploiement : utiliser CREATE2 pour un échange crypto

Créez un contrat intelligent distinct pour chaque utilisateur

Le déploiement d'un contrat intelligent distinct pour chaque utilisateur vous permet de ne pas stocker les clés privées des portefeuilles sur le serveur. L'échange appellera ce contrat intelligent pour transférer les jetons vers le hotwallet.

Nous avons également abandonné cette solution, car l'utilisateur ne peut pas voir son adresse de portefeuille sans déployer un contrat intelligent (cela est effectivement possible, mais de manière assez compliquée avec d'autres inconvénients que nous n'aborderons pas ici). Sur l'échange, l'utilisateur peut créer autant de comptes qu'il le souhaite, et chacun a besoin de son propre portefeuille. Cela signifie que nous devons dépenser de l'argent pour déployer un contrat sans même être sûr que l'utilisateur utilisera ce compte.

OpcodeCRÉER2

Pour résoudre le problème de la méthode précédente, nous avons décidé d'utiliser l'opcode CREATE2. CREATE2 vous permet de prédéterminer l'adresse où le contrat intelligent sera déployé. L'adresse est calculée à l'aide de la formule suivante :

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


, où:

  • propos — adresse du contrat intelligent qui appellera CREATE2
  • sel - valeur aléatoire
  • code_initial - bytecode de contrat intelligent pour le déploiement

Cela garantit que l'adresse que nous fournissons à l'utilisateur contiendra effectivement le bytecode souhaité. De plus, ce contrat intelligent peut être déployé à tout moment. Par exemple, lorsqu’un utilisateur décide d’utiliser son portefeuille pour la première fois.
Comment déterminer l'adresse d'un contrat intelligent avant le déploiement : utiliser CREATE2 pour un échange crypto
De plus, vous pouvez calculer l’adresse du contrat intelligent à chaque fois au lieu de la stocker, car :

  • propos dans la formule est constante car c'est l'adresse de notre usine de portefeuilles
  • sel - hachage user_id
  • code_initial est permanent puisque nous utilisons le même portefeuille

Plus d'améliorations

La solution précédente présente encore un inconvénient : le déploiement du smart contract est payant. Cependant, vous pouvez vous en débarrasser. Pour cela vous pouvez appeler la fonction transfert()et ensuite auto-destruction() dans le constructeur de portefeuille. Et puis le gaz nécessaire au déploiement du contrat intelligent sera restitué.

Contrairement à la croyance populaire, vous pouvez déployer plusieurs fois un contrat intelligent à la même adresse avec l'opcode CREATE2. En effet, CREATE2 vérifie que le nonce de l'adresse cible est nul (la valeur "1" lui est attribuée au début du constructeur). En même temps, la fonction auto-destruction() réinitialise l'adresse occasionnelle à chaque fois. Ainsi, si vous appelez à nouveau CREATE2 avec les mêmes arguments, la vérification occasionnelle réussira.

Veuillez noter que cette solution est similaire à la solution d'adresse Ethereum, mais sans qu'il soit nécessaire de stocker des clés privées. Le coût du transfert d'argent d'un portefeuille vers un hotwallet est approximativement égal au coût d'appel d'une fonction transfert(), puisque nous ne payons pas le déploiement du contrat intelligent.

Décision finale

Comment déterminer l'adresse d'un contrat intelligent avant le déploiement : utiliser CREATE2 pour un échange crypto

Initialement préparé :

  • fonction pour obtenir du sel par user_id
  • contrat intelligent qui appellera l'opcode CREATE2 avec le sel approprié (c'est-à-dire l'usine de portefeuille)
  • bytecode du portefeuille correspondant au contrat avec le constructeur suivant :

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


Pour chaque nouvel utilisateur, nous indiquons l'adresse de son portefeuille en calculant

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


Lorsque l'utilisateur transfère des jetons vers l'adresse du portefeuille correspondante, notre backend voit l'événement Transfer avec le paramètre égal à l’adresse du portefeuille. À ce stade, il est déjà possible d'augmenter le solde de l'utilisateur sur l'exchange avant de déployer le portefeuille.

Lorsque suffisamment de jetons s'accumulent dans l'adresse du portefeuille, nous pouvons les transférer tous en même temps vers le hotwallet. Pour ce faire, le backend appelle la fonction smart contract factory, qui effectue les actions suivantes :

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


Ainsi, on appelle le constructeur de contrat intelligent de portefeuille, qui transfère tous ses jetons à l'adresse du hotwallet puis s'autodétruit.

Le code complet peut être trouvé ici. Veuillez noter qu'il ne s'agit pas de notre code de production, car nous avons décidé d'optimiser le bytecode du portefeuille et de l'écrire dans les opcodes.

Auteur Pavel Kondratenkov, spécialiste d'Ethereum

Source: habr.com

Ajouter un commentaire