Як вызначыць адрас смарт-кантракту да дэплою: выкарыстанне CREATE2 для крыптабіржы

Тэма блокчейна не перастае быць крыніцай не толькі ўсялякага хайпа, але і вельмі каштоўных з тэхналагічнага пункта гледжання ідэй. Таму не абыйшла яна бокам і жыхароў сонечнага горада. Прыглядаюцца людзі, вывучаюць, спрабуюць перакласці сваю экспертызу ў традыцыйным інфабезе на блокчэйн-сістэмы. Пакуль што кропкава: адна з распрацовак "Ростелеком-Салар" умее правяраць бяспеку софту на базе блокчейна. А адначасна ўзнікаюць некаторыя думкі па рашэнні прыкладных задач блокчейн-супольнасці. Адным з такіх лайфхакаў - як вызначыць адрас смарт-кантракту да дэплою з дапамогай CREATE2 - сёння хачу з вамі падзяліцца пад катом.

Як вызначыць адрас смарт-кантракту да дэплою: выкарыстанне CREATE2 для крыптабіржы
Опкод CREATE2 быў дададзены ў хард-форцы Канстанцінопаль 28 лютага гэтага года. Як паказана ў EIP, гэты опкод быў уведзены ў асноўным для каналаў станаў (state channels). Аднак, мы выкарыстоўвалі яго для вырашэння іншай праблемы.

На біржы ёсць карыстачы з балансамі. Кожнаму карыстачу мы павінны падаць Ethereum-адрас, на які хто заўгодна зможа адпраўляць токены, тым самым папаўняючы свой акаўнт. Давайце назавем гэтыя адрасы "кашалькамі". Калі токены прыходзяць на кашалькі, мы павінны адправіць іх на адзіны кашалёк (hotwallet).

У наступных раздзелах я аналізую варыянты рашэння гэтай задачы без CREATE2 і расказваю, чаму мы адмовіліся ад іх. Калі вам цікавы толькі канчатковы вынік, вы можаце знайсці яго ў раздзеле "Выніковае рашэнне".

Ethereum-адрасы

Самае простае рашэнне - генераваць новыя ethereum-адрасы для новых карыстальнікаў. Гэтыя адрасы і будуць кашалькамі. Каб перавесці токены з кашалька ў hotwallet, неабходна падпісаць транзакцыю выклікам функцыі перадача() з прыватным ключом кашалька з бэкенда.

Гэты падыход мае наступныя перавагі:

  • гэта проста
  • кошт пераносу токенаў з кашалька на hotwallet роўная цане выкліку функцыі перадача()

Тым не менш, мы адмовіліся ад гэтага падыходу, паколькі ён мае адзін істотны недахоп: вам трэба недзе захоўваць прыватныя ключы. І справа не толькі ў тым, што яны могуць быць страчаныя, але яшчэ і ў тым, што вам неабходна старанна кіраваць доступам да гэтых ключоў. Калі хаця б адзін з іх скампраметаваны, то токены пэўнага карыстальніка не дасягнуць гарачага кашалька.

Як вызначыць адрас смарт-кантракту да дэплою: выкарыстанне CREATE2 для крыптабіржы

Ствараць асобны смарт-кантракт для кожнага карыстальніка

Разгортванне асобнага смарт-кантракту для кожнага карыстальніка дазваляе не захоўваць прыватныя ключы ад кашалькоў на серверы. Біржа выкліча гэты разумны кантракт для перадачы токенаў у hotwallet.

Ад гэтага рашэння мы таксама адмовіліся, паколькі карыстачу нельга паказаць адрас яго кашалька без разгортвання смарт-кантракту (гэта насамрэч магчыма, але даволі складанай выявай з іншымі недахопамі, якія мы не будзем тут абмяркоўваць). На біржы карыстач можа стварыць гэтулькі акаўнтаў, колькі яму трэба, і кожнаму патрэбен уласны кашалёк. Гэта азначае, што нам трэба марнаваць грошы на дэплой кантракту, нават не будучы ўпэўненымі, што карыстач будзе выкарыстоўваць гэты ўліковы запіс.

Опкод CREATE2

Каб ухіліць праблему папярэдняга спосабу, мы вырашылі выкарыстаць опкод CREATE2. CREATE2 дазваляе загадзя вызначыць адрас, па якім будзе разгорнуты смарт-кантракт. Адрас разлічваецца па наступнай формуле:

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


, дзе:

  • адрас - адрас смарт-кантракту, які будзе выклікаць CREATE2
  • соль - выпадковае значэнне
  • init_code - байт-код смарт-кантракту для разгортвання

Такім чынам гарантуецца, што адрас, які мы падаем карыстачу, сапраўды будзе ўтрымоўваць жаданы байт-код. Акрамя таго, гэты смарт-кантракт можа быць разгорнуты, калі нам трэба. Напрыклад, калі карыстач вырашыць упершыню выкарыстоўваць свой кашалёк.
Як вызначыць адрас смарт-кантракту да дэплою: выкарыстанне CREATE2 для крыптабіржы
Больш за тое, вы можаце разлічваць адрас смарт-кантракту кожны раз замест таго, каб захоўваць яго, бо:

  • адрас у формуле з'яўляецца пастаянным, так як гэта адрас нашай фабрыкі кашалькоў
  • соль - Хэш user_id
  • init_code з'яўляецца пастаянным, так як мы выкарыстоўваем адзін і той жа кашалёк

Больш паляпшэнняў

Папярэдняе рашэнне ўсё яшчэ мае адзін недахоп: вам трэба плаціць за разгортванне разумнага кантракту. Тым не менш, вы можаце пазбавіцца ад гэтага. Для гэтага вы можаце выклікаць функцыю перадача(), А затым selfdestruct() у канструктары кашалька. І тады газ за разгортванне смарт-кантракта будзе вернуты.

Насуперак распаўсюджанай памылцы, вы можаце разгарнуць смарт-кантракт па адным і тым жа адрасе некалькі разоў з опкодам CREATE2. Гэта злучана з тым, што CREATE2 правярае, што nonce мэтавага адрасу роўны нулю (яму прысвойваецца значэнне "1" у пачатку канструктара). Пры гэтым функцыя selfdestruct() кожны раз скідае nonce адрасы. Такім чынам, калі вы зноўку выклічаце CREATE2 з тымі ж аргументамі, праверка на nonce пройдзе.

Звярніце ўвагу, што гэтае рашэнне аналагічна варыянту з ethereum-адрасамі, але без неабходнасці захоўваць прыватныя ключы. Кошт пераводу грошай з кашалька на hotwallet прыкладна роўная кошту выкліку функцыі перадача(), паколькі мы не плацім за разгортванне смарт-кантракта.

Выніковае рашэнне

Як вызначыць адрас смарт-кантракту да дэплою: выкарыстанне CREATE2 для крыптабіржы

Першапачаткова падрыхтавана:

  • функцыя для атрымання солі па user_id
  • разумны кантракт, які будзе выклікаць опкод CREATE2 з адпаведнай соллю (г.зн. фабрыка кашалькоў)
  • байт-код кашалька, які адпавядае кантракту з наступным канструктарам:

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


Для кожнага новага карыстальніка мы паказваем яго / яе адрас кашалька шляхам разліку

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


Калі карыстач перакладае токены на які адпавядае адрас кашалька, наш бэкэнд бачыць падзею Transfer з параметрам _да, роўным адрасе кашалька. На гэты момант ужо магчыма павялічыць баланс карыстальніка на біржы да разгортвання кашалька.

Калі на адрасе кашалька назапашваецца дастатковая колькасць токенаў, мы можам перавесці іх усё адразу ў hotwallet. Для гэтага бэкенд выклікае функцыю фабрыкі смарт-кантрактаў, якая выконвае наступныя дзеянні:

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


Такім чынам, выклікаецца канструктар смарт-кантракту кашалька, які перадае ўсе свае токены на адрас hotwallet і затым самазнішчваецца.

Поўны код можна знайсці тут. Звярніце ўвагу, што гэта не наш прадакшн-код, бо мы вырашылі аптымізаваць байт-код кашалька і запісалі яго ў опкодах.

Аўтар Павел Кандраценкаў, спецыяліст у галіне Ethereum

Крыніца: habr.com

Дадаць каментар