Artikkeli · r8-evm-001
CREATE2: miten sopimuksen osoite voidaan tietää ennen luontia?
Ethereumissa sopimukselle voidaan laskea osoite jo ennen kuin siihen on luotu koodia. CREATE2 tekee sen luovan osoitteen, salt-arvon ja alustuskoodin perusteella. Ennustettava osoite ei kuitenkaan yksin todista, että sopimus on olemassa, että sen luonti onnistuu tai että sen toiminta on turvallista.[1]
Tässä kuvataan EIP-1014:n CREATE2-mekanismia ja jäljempänä EIP-6780:n SELFDESTRUCT-sääntöä vain verkossa ja lohkossa, jossa kyseiset säännöt ovat käytössä. Yksittäisen verkon, lohkon tai palvelun toteutusta ei ole tarkistettu. Esimerkit ovat ajatusharjoituksia, eivät ohjeita sopimuksen julkaisemiseen tai varojen lähettämiseen.
Mistä osoite syntyy?
CREATE2 on EVM:n luontioperaatio. Kaava on keccak256(0xff || luova_osoite || salt || keccak256(init_code))[12:]: etuliite 0xff on yksi tavu, luova osoite 20 tavua ja salt 32 tavua. Ulomman 32 tavun tiivisteen viimeiset 20 tavua muodostavat osoitteen.[1] Kyse on Keccak-256:sta, ei standardin FIPS SHA3-256:sta.
Luova osoite on CREATE2-operaation suorittavan tilin osoite. Tavallisessa tehdassopimuksen kutsussa se on tehdas, ei välttämättä tapahtuman aloittanut käyttäjä. Jos koodia suoritetaan delegatecall-kutsulla, koodi toimii kutsuvan sopimuksen osoitteen kontekstissa: ratkaise todellinen suoritusympäristö, älä oleta koodikirjaston osoitetta luojaksi.[1], [7]
Ulomman tiivisteen syöte on aina 85 tavua: 1 + 20 + 32 + 32. Viimeinen 32 tavun osa on alustuskoodin tiiviste, ei välttämättä itse alustuskoodin pituus. Salt puolestaan on luontioperaatiolle annettu 32 tavun arvo, ei käyttäjän salasana tai sopimuksen nimi.[1]
Vertailun vuoksi tavallisen CREATE-operaation osoitelasku perustuu luovaan osoitteeseen ja nonceen. CREATE2:n osoitekaavassa luovan tilin nonce ei ole vastaava erillinen syöte.[1] Noncea käytetään muualla myös tapahtumien järjestyksessä, mutta se on eri kysymys kuin tässä käsitelty CREATE2-osoitteen laskeminen.
Jos sama luova osoite käyttää samaa salt-arvoa ja täsmälleen samaa alustuskoodia, osoitelaskun syötteet ovat samat ja laskettu osoitekin on sama. Jos jokin osa muuttuu, osoite voi muuttua, mutta eri osoitetta ei taata: lyhennetyt tiivisteet voivat törmätä. Pelkkä samalla nimellä julkaistu sopimus ei osoita, että sen alustuskoodi olisi sama.[1]
Alustuskoodi ei ole sama kuin valmis sopimuskoodi
Alustuskoodi suoritetaan luomisen yhteydessä. Sen tuloksena syntyvä ajonaikainen tavukoodi on se koodi, joka tallennetaan luotavan sopimuksen tilaan. EIP-1014 erottaa nämä kaksi nimenomaisesti.[1]
Solidityssa constructor eli alustaja suoritetaan sopimuksen luomisen yhteydessä, ja alustajan ABI-koodatut argumentit liitetään luontikoodin jälkeen.[4] Kääntäjän versio ja asetukset, linkitetyt kirjastot sekä argumenttien koodaus voivat vaikuttaa tavuihin. Siksi on selvitettävä tarkat luontiin käytettävät tavut, ei pelkästään lähdekoodin nimeä tai kääntäjän versiota.
Alustuskoodin tiiviste sitoo osoitelaskun kyseiseen koodisyötteeseen. Samat tavut ja sama osoite eivät kuitenkaan takaa samaa alustettua tilaa tai ajonaikaista toimintaa kaikissa ympäristöissä: suoritettava alustuskoodi voi riippua ympäristöstä tai ulkoisesta tilasta.[1] Osoite ei myöskään ole auditointiraportti.
Ennustettu osoite ei takaa onnistunutta luontia
Kuvitellaan sopimus A, joka on tarkoitus luoda tietystä osoitteesta tietyllä salt-arvolla. Kehittäjä kertoo etukäteen laskeneensa osoitteen. Lukija voi ymmärtää väitteen tarkoituksen vasta, kun tiedossa ovat luova osoite, salt ja tarkka alustuskoodi. Tässä ei lasketa tai testata todellista osoitetta.
Vaikka nämä tiedot täsmäisivät, laskettu osoite ei itsessään osoita, että luonti on tapahtunut. EIP-684:n mukaan luonti epäonnistuu, jos kohdeosoitteen nonce ei ole nolla tai sen koodin pituus ei ole nolla.[2] Pelkkä koodin puuttuminen ei siis todista, että osoite on vapaa; saldo yksin ei ole tämä törmäysehto. Luonti voi epäonnistua muustakin syystä. Törmäyksessä virhe on määritelty virheellisen operaatiokoodin kaltaiseksi; se on eri tilanne kuin alustuskoodin REVERT.[2], [5] REVERT palauttaa CREATE2-operaatiolle nollan, jonka kutsuva koodi voi käsitellä, joten sisäisen luonnin epäonnistuminen ei yksin todista koko ulomman tapahtuman epäonnistumista.[5]
Älä siis tulkitse ilmoitusta ”osoite on ennustettu” samalla tavalla kuin vahvistettua havaintoa valmiista, toimivasta sopimuksesta. Tilanne voi myös muuttua ennen aiottua luontia, eikä tässä ole tutkittu tietyn osoitteen tilaa tai sopimuksen koodia.
Entä SELFDESTRUCT ja uudelleenluonti?
EIP-6780:n soveltuessa myöhemmässä tapahtumassa kuin luonti suoritettu SELFDESTRUCT pysäyttää kyseisen suorituskehyksen ja siirtää saldon kohteelle, mutta ei poista tiliä, koodia tai tallennustilaa. Jos kohde on sopimus itse, saldon nettomuutos on nolla.[6]
Poikkeus koskee samassa tapahtumassa luotua sopimusta: silloin aiempi poistokäyttäytyminen säilyy, ja jos saldo ohjataan sopimukselle itselleen, se poltetaan.[6] Poikkeus ei kuitenkaan takaa uudelleenluontia: SELFDESTRUCT ei poista noncea ja koodia välittömästi, joten samalla tapahtumalla tapahtuvaa luontia ja uudelleenluontia ei voi olettaa mahdolliseksi.[1] Älä myöskään oleta, että kaikki uudelleenluonti olisi poikkeuksetta mahdotonta. Soveltuva verkko, lohko ja fork on tarkistettava erikseen.[6]
Mitä tiedosta kannattaa päätellä?
Erottele ensin kolme väitettä: osoite on laskettavissa, sopimus on luotu kyseiseen verkkoon ja sopimus tekee sen, mitä sen väitetään tekevän. Ne vaativat eri selvityksiä. Myös luovan osoitteen ja laskennassa käytettyjen tavujen alkuperä on voitava varmistaa.
Jos jokin tieto puuttuu, tulos jää avoimeksi. Samannäköinen osoite toisessa verkossa ei sellaisenaan todista, että siellä olisi sama tila tai sama toteutus. Älä lähetä varoja pelkän ennustetun osoitteen tai tuntemattoman osapuolen ohjeen perusteella. Osoitteen matemaattinen määräytyminen on hyödyllinen ominaisuus, mutta se ei korvaa kyseisen järjestelmän teknistä tarkistusta.
Lähteet
- [1] EIP-1014 Skinny CREATE2 — Specification; Examples; Clarifications
- [2] EIP-684 Revert creation in case of collision — Specification
- [4] Solidity 0.8.30 Contracts — Creating Contracts
- [5] EIP-140 REVERT instruction — Specification
- [6] EIP-6780 SELFDESTRUCT only in same transaction — Specification
- [7] Solidity 0.8.30 Introduction to Smart Contracts — Delegatecall and Libraries