Cum să determinați adresa unui contract inteligent înainte de implementare: folosind CREATE2 pentru un schimb cripto

Subiectul blockchain-ului nu încetează să fie o sursă nu numai de tot felul de hype, ci și de idei foarte valoroase din punct de vedere tehnologic. Prin urmare, ea nu a ocolit locuitorii orașului însorit. Oamenii se uită îndeaproape, studiază, încearcă să-și schimbe expertiza în baza tradițională de informații către sistemele blockchain. Până acum, punctual: una dintre dezvoltările Rostelecom-Solar este capabilă să verifice securitatea software-ului bazat pe blockchain. Și pe parcurs, apar câteva gânduri cu privire la rezolvarea problemelor aplicate ale comunității blockchain. Unul dintre aceste trucuri de viață - cum să determinați adresa unui contract inteligent înainte de implementare folosind CREATE2 - astăzi vreau să vă împărtășesc cu dvs.

Cum să determinați adresa unui contract inteligent înainte de implementare: folosind CREATE2 pentru un schimb cripto
Opcode-ul CREATE2 a fost adăugat în hard fork-ul Constantinopol pe 28 februarie a acestui an. După cum se precizează în EIP, acest opcode a fost introdus în primul rând pentru canalele de stat. Cu toate acestea, l-am folosit pentru a rezolva o altă problemă.

Există utilizatori cu solduri la schimb. Trebuie să furnizăm fiecărui utilizator o adresă Ethereum, la care oricine poate trimite jetoane, reumplendu-și astfel contul. Să numim aceste adrese „portofele”. Când jetoanele ajung în portofel, trebuie să le trimitem într-un singur portofel (hotwallet).

În secțiunile următoare, analizez opțiunile pentru rezolvarea acestei probleme fără CREATE2 și vă spun de ce le-am abandonat. Dacă sunteți interesat doar de rezultatul final, îl puteți găsi în secțiunea „Soluția finală”.

Adrese Ethereum

Cea mai simplă soluție este să generezi noi adrese Ethereum pentru noii utilizatori. Aceste adrese vor fi portofelele. Pentru a transfera jetoane din portofel în portofel, trebuie să semnați tranzacția apelând funcția transfer() cu cheia privată a portofelului din backend.

Această abordare are următoarele avantaje:

  • e simplu
  • costul transferului de jetoane din portofel în portofel este egal cu costul apelării funcției transfer()

Cu toate acestea, am abandonat această abordare, deoarece are un dezavantaj semnificativ: trebuie să stocați cheile private undeva. Și nu este doar faptul că acestea pot fi pierdute, ci și că trebuie să gestionați cu atenție accesul la aceste chei. Dacă cel puțin unul dintre ele este compromis, atunci jetoanele unui anumit utilizator nu vor ajunge în portofelul fierbinte.

Cum să determinați adresa unui contract inteligent înainte de implementare: folosind CREATE2 pentru un schimb cripto

Creați un contract inteligent separat pentru fiecare utilizator

Implementarea unui contract inteligent separat pentru fiecare utilizator vă permite să nu stocați cheile private din portofele pe server. Schimbul va apela acest contract inteligent pentru a transfera jetoanele pe hotwallet.

Am abandonat și această soluție, deoarece utilizatorului nu i se poate afișa adresa portofelului fără a implementa un smart contract (acest lucru este de fapt posibil, dar într-un mod destul de complicat cu alte dezavantaje pe care nu le vom discuta aici). Pe schimb, utilizatorul își poate crea câte conturi are nevoie, iar fiecare are nevoie de propriul portofel. Aceasta înseamnă că trebuie să cheltuim bani pentru implementarea unui contract fără să fim măcar siguri că utilizatorul va folosi acest cont.

Opcode CREATE2

Pentru a rezolva problema metodei anterioare, am decis să folosim opcode-ul CREATE2. CREATE2 vă permite să predeterminați adresa unde va fi implementat contractul inteligent. Adresa se calculează folosind următoarea formulă:

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


, unde:

  • adresa — adresa contractului inteligent care va apela CREATE2
  • sare - valoare aleatorie
  • init_code - cod octet contract inteligent pentru implementare

Acest lucru asigură că adresa pe care o oferim utilizatorului va conține de fapt bytecode-ul dorit. De asemenea, acest contract inteligent poate fi implementat oricând avem nevoie. De exemplu, atunci când un utilizator decide să-și folosească portofelul pentru prima dată.
Cum să determinați adresa unui contract inteligent înainte de implementare: folosind CREATE2 pentru un schimb cripto
Mai mult, puteți calcula adresa smart contract de fiecare dată în loc să o stocați, deoarece:

  • adresa în formulă este constantă deoarece este adresa fabricii noastre de portofel
  • sare - hash user_id
  • init_code este permanent deoarece folosim același portofel

Mai multe îmbunătățiri

Soluția anterioară are încă un dezavantaj: trebuie să plătiți pentru implementarea unui contract inteligent. Cu toate acestea, puteți scăpa de el. Pentru aceasta puteți apela funcția transfer(), și apoi autodistrugere() în constructorul portofelului. Și apoi gazul pentru desfășurarea contractului inteligent va fi returnat.

Contrar credinței populare, puteți implementa un contract inteligent la aceeași adresă de mai multe ori cu opcode-ul CREATE2. Acest lucru se datorează faptului că CREATE2 verifică dacă nonce al adresei țintă este zero (i se atribuie valoarea „1” la începutul constructorului). În același timp, funcția autodistrugere() resetează adresa nonce de fiecare dată. Astfel, dacă apelați din nou CREATE2 cu aceleași argumente, verificarea nonce va trece.

Vă rugăm să rețineți că această soluție este similară cu soluția de adrese Ethereum, dar fără a fi nevoie să stocați chei private. Costul transferului de bani dintr-un portofel într-un portofel este aproximativ egal cu costul apelării unei funcții transfer(), deoarece nu plătim pentru implementarea contractului inteligent.

Decizia finala

Cum să determinați adresa unui contract inteligent înainte de implementare: folosind CREATE2 pentru un schimb cripto

Inițial pregătit:

  • funcția de a obține sare user_id
  • contract inteligent care va apela codul operațional CREATE2 cu sarea corespunzătoare (adică fabrica de portofel)
  • codul octet portofel corespunzător contractului cu următorul constructor:

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


Pentru fiecare utilizator nou, arătăm adresa portofelului său prin calcul

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


Când utilizatorul transferă jetoane la adresa portofelului corespunzătoare, backend-ul nostru vede evenimentul Transfer cu parametrul _laegal cu adresa portofelului. În acest moment, este deja posibilă creșterea soldului utilizatorului pe schimb înainte de a implementa portofelul.

Când se acumulează suficiente token-uri în adresa portofelului, le putem transfera pe toate odată în hotwallet. Pentru a face acest lucru, backend-ul apelează funcția Smart Contract Factory, care efectuează următoarele acțiuni:

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


Astfel, este apelat constructorul de smart contract de portofel, care își transferă toate token-urile la adresa hotwallet și apoi se autodistruge.

Codul complet poate fi găsit aici. Vă rugăm să rețineți că acesta nu este codul nostru de producție, deoarece am decis să optimizăm bytecode-ul portofelului și să îl scriem în opcodes.

Autorul Pavel Kondratenkov, specialist Ethereum

Sursa: www.habr.com

Adauga un comentariu