В Ethereum до недавнього часу ресурси були обмежені і встановлювалися за ціною, використовуючи один ресурс під назвою «газ». Газ — це міра кількості «обчислювальних зусиль», необхідних для обробки певної транзакції або блоку. Газ об'єднує кілька видів «зусиль», зокрема:
Наприклад, ця транзакціящо я відправив, коштував усього 47,085 газу. Це розподілено між (i) “базовим коштом” у розмірі 21000 газу, (ii) 1556 газу за байти в усьому calldata, включеному як частина транзакції (iii) 16500 газу на зчитування та запис у сховище, (iv) газу 2149 на створенняжурнал, а решта - для виконання EVM. Плата за транзакцію, яку користувач повинен заплатити, пропорційна газу, який витрачається на транзакцію. Блок може містити до максимуму 30 мільйонів газу, а ціни на газ постійно коригуються через @vbuterin/eip-1559-faq">Механізм націлювання EIP-1559, який забезпечує, що в середньому блоки містять 15 мільйонів газу.
Цей підхід має одну основну ефективність: оскільки все об'єднано в один віртуальний ресурс, це призводить до дуже простого дизайну ринку. Оптимізація транзакції для мінімізації витрат є простою, оптимізація блоку для збору найвищих можливих комісій є відносно простою (за винятком MEV) та немає дивних стимулів, які б допомагали деяким транзакціям пакуватися разом з іншими, щоб заощадити на комісіях.
Але цей підхід також має одну основну неефективність: він розглядає різні ресурси як взаємозамінні, коли фактичні обмеження того, що мережа може обробляти, насправді не такі. Один з способів зрозуміти цю проблему - розглянути цю діаграму:
Ліміт газу накладає обмеження на
𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗𝑐𝑜𝑚𝑝𝑢𝑢тāтіōn<𝑁
Фактичне обмеження безпеки підтримується частіше
макс(х1*дата,х2*компутація)<Н
. Це розбіжність призводить до того, що ліміт газу непотрібно виключає дійсно безпечні блоки, або приймає дійсно небезпечні блоки, або якусь суміш обох.
Якщо є
𝑛
ресурси, які мають визначені межі безпеки, то одновимірний газ ймовірно зменшує пропускну здатність до фактора
𝑛
Для цієї причини вже довгий час існує інтерес до концепції багатовимірного газу, і разом зEIP-4844на сьогодні на Ethereum ми фактично використовуємо багатовимірний газ. Цей допис досліджує переваги цього підходу та перспективи його подальшого збільшення.
На початку цього року середній блок був 150 кБ у розмірі. Велика частина цього розміру - це дані rollup: протоколи 2 рівнязберігання даних на ланцюжку для забезпечення безпеки. Ці дані були дорогими: навіть якщо операції на rollups коштували б ~5-10 разів менше, ніж відповідні операції на Ethereum L1, навіть ця вартість була занадто високою для багатьох випадків використання.
Чому б не зменшити вартість газу для calldata (наразі 16 газу за ненульовий байт і 4 газу за нульовий байт), щоб зробити rollups дешевшими? Ми це робив раніше, ми могли б зробити це знову. Відповідь тут: найгірший розмір блоку був
30,000,00016=1,875,000
ненульові байти, і мережа вже майже не може впоратися з блоками такого розміру. Зменшення витрат ще в 4 рази підвищить максимум до 7,5 МБ, що буде величезним ризиком для безпеки.
Цю проблему вдалося вирішити шляхом введення окремого простору даних, який сприятливий для rollup, відомого як "крапки", в кожен блок. Два ресурси мають окремі ціни та окремі обмеження: після хардфорка Dencun блок Ethereum може містити максимум (i) 30 мільйонів газу, та (ii) 6 крапок, які можуть містити ~125 кБ кожна кальдата. Обидва ресурси мають окремі ціни, кориговані зарозділити механізми ціноутворення, схожі на EIP-1559, спрямовуючись на середнє використання 15 мільйонів газу та 3 блобів на блок.
В результаті ролапи стали у 100 разів дешевшими, обсяг транзакцій на ролапах збільшився більш ніж у 3 рази, а теоретичний максимальний розмір блоку було збільшено трохи: з ~1,9 МБ до ~2,6 МБ.
Вартість транзакцій на rollups, на вартість growthepie.xyz. Відгалуження Dencun, яке ввело краплі з багатовимірним ціноутворенням, сталося 13 березня 2024 року.
У найближчому майбутньому виникне схожа проблема щодо збереження доказів для безстандартних клієнтів. Безстандартні клієнти - це новий тип клієнта, який зможе перевіряти ланцюг без зберігання багато чи будь-яких даних локально. Безстандартні клієнти роблять це, приймаючи докази конкретних частин стану Ethereum, які транзакції в тому блоку повинні торкнутися.
Клієнт без стану отримує блок разом з доказами, що підтверджують поточні значення в конкретних частинах стану (наприклад, баланси рахунків, код, сховище), які задіяні в виконанні блоку. Це дозволяє вузлу перевірити блок без наявності власного сховища.
Витрата на читання зберігання становить від 2100 до 2600 газу в залежності від типу читання, а витрати на запис зберігання вищі. У середньому блок виконує близько 1000 читань та записів зберігання (включаючи перевірку балансу ETH, виклики SSTORE та SLOAD, читання коду контракту та інші операції). Теоретичний максимум, проте, становить
30,000,0002,100=14,285
читає. Навантаження на пропускну здатність безстійкого клієнта прямо пропорційне цьому числу.
Сьогодні планується підтримка клієнтів без стану шляхом перенесення дизайну дерева стану Ethereum з Дерева Меркла-ПатріціїдоVerkle trees. Однак, дерева Веркле не є квантово-стійкими і не є оптимальними для нових хвиль систем доведення STARK. В результаті багато людей зацікавлені в підтримці безстаних клієнтів через бінарні дерева Меркле та STARKsзамість цього - або цілком пропустивши Веркле, або оновивши через кілька років після переходу до Веркле, коли STARKs стануть більш зрілими.
Докази гілок бінарного хеш-дерева STARK мають багато переваг, але у них є ключовий недолік - генерація доказів займає довгий час: в той час, як Дерева Верклеможе довестипонад сто тисяч значень на секунду, хеш-базові STARKs зазвичай можуть довести лише кілька тисяч хешів за секунду, і для доведення кожного значення потрібен «гіллястий» вузол, що містить багато хешів.
Дані числа, які сьогодні прогнозуються з гіпероптимізованих доказових систем, таких як Binius та Plonky3та спеціалізовані хеші, такі як Vision-Mark-32, здається, що ми ще деякий час будемо в режимі, коли практично можна довести 1,000 значень менш ніж за секунду, але не 14,285 значень. Середні блоки були б добре, але блоки у найгіршому випадку, які можуть бути опубліковані зловмисником, можуть зламати мережу.
"Стандартним" способом, яким ми керувалися в такому сценарії, було переоцінення: зробити зчитування зберігання більш дорогим для зменшення максимального значення на блок до безпечного рівня. Однак ми маємо вжезробленоцебагаторази, і це зробить занадто багато програм занадто дорогими, щоб робити це знову. Кращим підходом був би багатовимірний газ: обмеження та оплата окремо за доступ до сховища, тримаючи середнє використання на рівні 1,000 доступів до сховища на блок, але встановлюючи межу на блок, наприклад, 2,000.
Ще один ресурс, над яким варто подумати, - це збільшення розміру стану: операції, які збільшують розмір стану Ethereum, який повні вузли будуть зобов'язані утримувати з того часу. Унікальна властивість збільшення розміру стану полягає в тому, що обґрунтування обмеження цього процесу повністю випливає з тривалого сталого використання, а не зі сплесків. Тому може бути корисність у добавленні окремого газового параметра для операцій збільшення розміру стану (наприклад, зміна з нуля на ненуль у SSTORE, створення контракту), але з іншою метою: ми могли б встановити плаваючу ціну для досягнення певного середнього використання, але взагалі не встановлювати обмеження на кожний блок.
Це показує одну з потужних властивостей багатовимірного газу: це дозволяє нам окремо задавати питання (i) яке є ідеальним середнім використанням і (ii) яке є безпечним максимальним використанням на блок для кожного ресурсу. Замість того, щоб встановлювати ціни на газ на основі максимумів на блок та дозволяти середньому використанню слідувати за ним, ми маємо
2𝑛
ступені свободи для встановлення
2𝑛
параметри, налаштовуючи кожен з них на основі того, що є безпечним для мережі.
Більш складні ситуації, наприклад, коли два ресурси мають урахування безпеки, які частково додаються, можуть бути вирішені шляхом надання опкоду або вартості ресурсу певної кількості різних типів газу (наприклад, збереження від нуля до ненульового SSTORE може коштувати 5000 газу, який підтверджує бездоганність клієнта, та 20000 газу для розширення сховища).
Дозвольте
𝑥1
бути вартістю газу для даних та
𝑥2
бути вартістю газових обчислень, тому в одновимірній газовій системі ми можемо записати вартість газу для транзакції:
газ=х1*дата+х2*компутація
У цій схемі ми замість цього визначаємо вартість газу транзакції як:
газ=макс(х1*дата,х2*компутація)
Тобто замість оплати транзакції за дані плюс обчислення транзакція оплачується на основі того, яку з двох ресурсів вона використовує більше. Це легко можна розширити, щоб охопити більше вимірів (наприклад,
макс(…,х3*стораге_аксес)
)
Має бути легко бачити, як це покращує пропускну здатність, зберігаючи безпеку. Теоретично максимальна кількість даних в блоку все ще
газлімітх1
, точно так само, як і в одновимірній схемі газу. Так само, теоретична максимальна кількість обчислень є
ГАЗОВИЙ ЛІМІТx2
, знову точно так само, як і в одновимірній схемі газу. Однак вартість газу будь-якої транзакції, яка споживає як дані, так і обчислення, зменшується.
Це приблизно схема, що використовується в запропонованій EIP-7623, щоб зменшити максимальний розмір блоку, збільшуючи кількість блоків. Точний механізм в EIP-7623 трохи складніший: він зберігає поточну ціну calldata у розмірі 16 газу за байт, але додає «мінімальну ціну» у розмірі 48 газу за байт; транзакція сплачує більш високу ціну (16 bytes + виконавчий газ) та (48 байт). В результаті EIP-7623 зменшує теоретичний максимальний обсяг даних транзакції в блоку з приблизно 1,9 МБ до приблизно 0,6 МБ, залишаючи витрати більшості додатків без змін. Перевага такого підходу полягає в тому, що це дуже маленька зміна від поточної одновимірної схеми газу, тому вона дуже легко впроваджується.
Є два недоліки:
Я б сказав, що правило у стилі EIP-7623, як для даних виклику транзакції, так і для інших ресурсів, може принести достатньо великі переваги, щоб варто було це навіть попри ці недоліки. Однак, якщо і коли ми готові вкласти (значно більше) зусиль у розробку, існує більш ідеальний підхід.
Давайте спочатку підсумуємо, як працює «звичайний» EIP-1559. Ми зосередимося на версії, яка була введена в EIP-4844 для крапельок, оскільки математично вона більш елегантна.
Ми відстежуємо параметр, excess_blobs. Протягом кожного блоку ми встановлюємо:
excess_blobs <— макс(перебільшені_крапки + len(блок.крапки) - ЦІЛЬ, 0)
Де TARGET = 3. Це означає, що якщо блок має більше куль, ніж ціль, excess_blobs збільшується, а якщо блок має менше, ніж ціль, воно зменшується. Потім ми встановлюємо blob_basefee = exp(excess_blobs / 25.47), де exp є наближенням експоненційної функції
𝑒𝑥𝑝(𝑥)=2.71828𝑥
.
Це означає, що кожного разу, коли excess_blobs збільшується на ~25, базова комісія за краплу збільшується в ~2,7 раза. Якщо краплі стають занадто дорогими, середнє використання падає, і excess_blobs починає зменшуватися, автоматично знижуючи ціну знову. Ціна краплі постійно коригується, щоб переконатися, що у середньому блоки наполовину заповнені - тобто вони містять у середньому по 3 краплі кожна.
Якщо використання тимчасово зросте, то вступає в силу обмеження: кожний блок може містити максимум 6 кульок, і в такому випадку транзакції можуть конкурувати між собою, підвищуючи свої пріоритетні комісії. У звичайному випадку кожній кульці досить заплатити базову комісію для кульки плюс невелику додаткову пріоритетну комісію як стимул для включення взагалі.
Цей вид ціноутворення існував в Ethereum для газу протягом років: дуже схожий механізм було введено з @vbuterin/eip-1559-faq">EIP-1559 повернувся у 2020 році. З EIP-4844 у нас тепер є дві окремо плаваючі ціни на газ та на блоки.
Базова комісія за газ протягом однієї години на 2024-05-08, в гвей. Джерело: ultrasound.money.
В принципі ми можемо додати ще окремі плаваючі комісії за зчитування зберігання та інші види операцій, хоча з одним застереженням, про яке я розширю в наступному розділі.
Для користувачів досвід дуже схожий на сьогодні: замість оплати однієї базової плати ви платите дві базові плати, але ваш гаманець може абстрагувати це від вас і показати лише очікувану плату та максимальну плату, яку ви можете очікувати.
Для будівельників блоків, в більшості випадків оптимальна стратегія та сама, що й сьогодні: включати все, що є дійсним. Більшість блоків не є повними - ні один в газінів крапляхОдним з викликів є випадок, коли є достатньо газу або достатньо крапель, щоб перевищити обмеження блоку, і забудовник повинен потенційно вирішити Багатовимірна задача про рюкзакщоб максимізувати свій прибуток. Однак навіть там існують досить хороші апроксимаційні алгоритми, і вигоди від створення власних алгоритмів для оптимізації прибутку в цьому випадку набагато менші, ніж вигоди від того самого зроблення з МЕV.
Для розробників основним викликом є потреба переконструювати можливості EVM та його навколишню інфраструктуру, які сьогодні розроблені навколо однієї ціни та одного ліміту, в проект, що враховує кілька цін та кілька лімітів. Одна з проблем для розробників додатків полягає в тому, що оптимізація стає трохи складнішою: у деяких випадках ви вже не зможете однозначно сказати, що A ефективніший за B, оскільки якщо A використовує більше calldata, але B використовує більше виконання, то A може бути дешевшим, коли calldata дешевий, і дорожчим, коли calldata дорогий. Однак розробники все ще зможуть отримати досить гарні результати, оптимізуючи на основі середньострокових історичних середніх цін.
Є одна проблема, яка не виникла з блобами, і не виникне з EIP-7623 або навіть з «повною» багатовимірною реалізацією ціноутворення для calldata, але виникне, якщо ми спробуємо окремо встановити ціну на доступ до стану або будь-які інші ресурси: обмеження газу в підвикликах.
Ліміти газу в EVM існують в двох місцях. По-перше, кожна транзакція встановлює ліміт газу, який обмежує загальну кількість газу, який може бути використаний у цій транзакції. По-друге, коли контракт викликає інший контракт, виклик може встановити свій власний ліміт газу. Це дозволяє контрактам викликати інші контракти, яким вони не довіряють, і все ж гарантувати, що у них залишиться газ для виконання інших обчислень після цього виклику.
Слід угоди з абстракцією рахунку, де рахунок викликає інший рахунок, і надає викликачеві обмежену кількість газу, щоб забезпечити можливість зовнішнього виклику продовжувати працювати навіть якщо викликач витрачає весь газ, який був виділений для нього.
Виклик полягає в тому, щоб зробити газ багатовимірним між різними типами виконання, здається, що це потребуватиме підвикликів для надання кількох обмежень для кожного типу газу, що потребуватиме дуже глибоких змін у EVM і не буде сумісним з існуючими додатками.
Це одна з причин, чому багатовимірні пропозиції щодо газу часто зупиняються на двох вимірах: даних та виконанні. Дані (чи то дані виклику транзакції, чи блоби) призначені поза EVM, тому нічого всередині EVM не потрібно змінювати, щоб окремо цінувати дані виклику транзакції або блоби.
Ми можемо подумати про «рішення у стилі EIP-7623» для цієї проблеми. Ось одна проста реалізація: під час виконання збільште вартість операцій зберігання в 4 рази; для спрощення аналізу скажемо, що 10000 газу на операцію зберігання. В кінці транзакції повертати min(7500 * операції_зберігання, виконання_газу). Результат буде таким, що після відрахування повернення користувач оплачує:
execution_газ + 10000 операції зберігання - мін(7500 операції зберігання, виконання газу)
Яке дорівнює:
макс (виконання_газ + 2500 операції зберігання, 10000storage_operations)
Це віддзеркалює структуру EIP-7623. Інший спосіб зробити це - відстежувати storage_operations та execution_gas в реальному часі, та стягувати 2500 або 10000, залежно від того, скільки максимально (execution_gas + 2500)операції зберігання, 10000операції зберігання) збільшується в момент виклику опкоду. Це уникне потреби в транзакціях перерозподілити газ, який вони в основному отримають назад у вигляді відшкодувань.
Ми не отримуємо деталізованого дозволу для підвикликів: підвиклик може використати всю "дозволену кількість" угоди для дешевих операцій зберігання. Але ми отримуємо щось достатньо добре, де контракт, який робить підвиклик, може встановити обмеження та забезпечити, що після завершення виконання підвиклику у головного виклику все ще є достатньо газу для виконання будь-якої післяопрацювання, яке потрібно зробити.
Найпростіше «повне багатовимірне рішення ціноутворення», яке я можу уявити, полягає в тому, що ми розглядаємо ліміти викликів підпрограм як пропорційні. Тобто, припустимо, що є
𝑘
різні типи виконання, і кожна транзакція встановлює багатовимірний ліміт
𝐿1…𝐿𝑘
. Припустимо, що в даний момент виконання залишок газу становить
𝑔1…𝑔𝑘
Припустимо, що виклик opcode викликається з лімітом газу для підвиклику.
𝑆
. Дозвольте
𝑠1=𝑆
, а потім
𝑠2=𝑠1газ1∗газ2
,
𝑠3=𝑠1газ1∗газ3
, і так далі.
Це означає, що ми розглядаємо перший тип газу (реалістично, виконання VM) як певний вид привілейованого «одиниці обліку», а потім призначаємо інші типи газу так, щоб підвиклик отримував той самий відсоток доступного газу по кожному типу. Це трохи некрасиво, але це максимізує зворотню сумісність. Якщо ми хочемо зробити схему більш «нейтральною» між різними типами газу, за рахунок жертвування зворотною сумісністю, ми можемо просто зробити параметр обмеження газу підвиклика представляти дріб (наприклад, [1…63] / 64) від залишкового газу в поточному контексті).
У будь-якому випадку, проте, варто підкреслити, що після початку використання багатовимірного виконавчого газу рівень внутрішньої потворності збільшується, і це, здається, важко уникнути. Таким чином, наше завдання - зробити складений компроміс: чи ми приймаємо трохи більше потворності на рівні EVM, щоб безпечно розблокувати значні переваги масштабованості L1, і, якщо так, який саме конкретний пропозиція працює найкраще для економіки протоколу та розробників додатків? Дуже ймовірно, це не одна з тих, про які я згадував вище, і все ще є можливість придумати щось більш елегантне та краще.
В Ethereum до недавнього часу ресурси були обмежені і встановлювалися за ціною, використовуючи один ресурс під назвою «газ». Газ — це міра кількості «обчислювальних зусиль», необхідних для обробки певної транзакції або блоку. Газ об'єднує кілька видів «зусиль», зокрема:
Наприклад, ця транзакціящо я відправив, коштував усього 47,085 газу. Це розподілено між (i) “базовим коштом” у розмірі 21000 газу, (ii) 1556 газу за байти в усьому calldata, включеному як частина транзакції (iii) 16500 газу на зчитування та запис у сховище, (iv) газу 2149 на створенняжурнал, а решта - для виконання EVM. Плата за транзакцію, яку користувач повинен заплатити, пропорційна газу, який витрачається на транзакцію. Блок може містити до максимуму 30 мільйонів газу, а ціни на газ постійно коригуються через @vbuterin/eip-1559-faq">Механізм націлювання EIP-1559, який забезпечує, що в середньому блоки містять 15 мільйонів газу.
Цей підхід має одну основну ефективність: оскільки все об'єднано в один віртуальний ресурс, це призводить до дуже простого дизайну ринку. Оптимізація транзакції для мінімізації витрат є простою, оптимізація блоку для збору найвищих можливих комісій є відносно простою (за винятком MEV) та немає дивних стимулів, які б допомагали деяким транзакціям пакуватися разом з іншими, щоб заощадити на комісіях.
Але цей підхід також має одну основну неефективність: він розглядає різні ресурси як взаємозамінні, коли фактичні обмеження того, що мережа може обробляти, насправді не такі. Один з способів зрозуміти цю проблему - розглянути цю діаграму:
Ліміт газу накладає обмеження на
𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗𝑐𝑜𝑚𝑝𝑢𝑢тāтіōn<𝑁
Фактичне обмеження безпеки підтримується частіше
макс(х1*дата,х2*компутація)<Н
. Це розбіжність призводить до того, що ліміт газу непотрібно виключає дійсно безпечні блоки, або приймає дійсно небезпечні блоки, або якусь суміш обох.
Якщо є
𝑛
ресурси, які мають визначені межі безпеки, то одновимірний газ ймовірно зменшує пропускну здатність до фактора
𝑛
Для цієї причини вже довгий час існує інтерес до концепції багатовимірного газу, і разом зEIP-4844на сьогодні на Ethereum ми фактично використовуємо багатовимірний газ. Цей допис досліджує переваги цього підходу та перспективи його подальшого збільшення.
На початку цього року середній блок був 150 кБ у розмірі. Велика частина цього розміру - це дані rollup: протоколи 2 рівнязберігання даних на ланцюжку для забезпечення безпеки. Ці дані були дорогими: навіть якщо операції на rollups коштували б ~5-10 разів менше, ніж відповідні операції на Ethereum L1, навіть ця вартість була занадто високою для багатьох випадків використання.
Чому б не зменшити вартість газу для calldata (наразі 16 газу за ненульовий байт і 4 газу за нульовий байт), щоб зробити rollups дешевшими? Ми це робив раніше, ми могли б зробити це знову. Відповідь тут: найгірший розмір блоку був
30,000,00016=1,875,000
ненульові байти, і мережа вже майже не може впоратися з блоками такого розміру. Зменшення витрат ще в 4 рази підвищить максимум до 7,5 МБ, що буде величезним ризиком для безпеки.
Цю проблему вдалося вирішити шляхом введення окремого простору даних, який сприятливий для rollup, відомого як "крапки", в кожен блок. Два ресурси мають окремі ціни та окремі обмеження: після хардфорка Dencun блок Ethereum може містити максимум (i) 30 мільйонів газу, та (ii) 6 крапок, які можуть містити ~125 кБ кожна кальдата. Обидва ресурси мають окремі ціни, кориговані зарозділити механізми ціноутворення, схожі на EIP-1559, спрямовуючись на середнє використання 15 мільйонів газу та 3 блобів на блок.
В результаті ролапи стали у 100 разів дешевшими, обсяг транзакцій на ролапах збільшився більш ніж у 3 рази, а теоретичний максимальний розмір блоку було збільшено трохи: з ~1,9 МБ до ~2,6 МБ.
Вартість транзакцій на rollups, на вартість growthepie.xyz. Відгалуження Dencun, яке ввело краплі з багатовимірним ціноутворенням, сталося 13 березня 2024 року.
У найближчому майбутньому виникне схожа проблема щодо збереження доказів для безстандартних клієнтів. Безстандартні клієнти - це новий тип клієнта, який зможе перевіряти ланцюг без зберігання багато чи будь-яких даних локально. Безстандартні клієнти роблять це, приймаючи докази конкретних частин стану Ethereum, які транзакції в тому блоку повинні торкнутися.
Клієнт без стану отримує блок разом з доказами, що підтверджують поточні значення в конкретних частинах стану (наприклад, баланси рахунків, код, сховище), які задіяні в виконанні блоку. Це дозволяє вузлу перевірити блок без наявності власного сховища.
Витрата на читання зберігання становить від 2100 до 2600 газу в залежності від типу читання, а витрати на запис зберігання вищі. У середньому блок виконує близько 1000 читань та записів зберігання (включаючи перевірку балансу ETH, виклики SSTORE та SLOAD, читання коду контракту та інші операції). Теоретичний максимум, проте, становить
30,000,0002,100=14,285
читає. Навантаження на пропускну здатність безстійкого клієнта прямо пропорційне цьому числу.
Сьогодні планується підтримка клієнтів без стану шляхом перенесення дизайну дерева стану Ethereum з Дерева Меркла-ПатріціїдоVerkle trees. Однак, дерева Веркле не є квантово-стійкими і не є оптимальними для нових хвиль систем доведення STARK. В результаті багато людей зацікавлені в підтримці безстаних клієнтів через бінарні дерева Меркле та STARKsзамість цього - або цілком пропустивши Веркле, або оновивши через кілька років після переходу до Веркле, коли STARKs стануть більш зрілими.
Докази гілок бінарного хеш-дерева STARK мають багато переваг, але у них є ключовий недолік - генерація доказів займає довгий час: в той час, як Дерева Верклеможе довестипонад сто тисяч значень на секунду, хеш-базові STARKs зазвичай можуть довести лише кілька тисяч хешів за секунду, і для доведення кожного значення потрібен «гіллястий» вузол, що містить багато хешів.
Дані числа, які сьогодні прогнозуються з гіпероптимізованих доказових систем, таких як Binius та Plonky3та спеціалізовані хеші, такі як Vision-Mark-32, здається, що ми ще деякий час будемо в режимі, коли практично можна довести 1,000 значень менш ніж за секунду, але не 14,285 значень. Середні блоки були б добре, але блоки у найгіршому випадку, які можуть бути опубліковані зловмисником, можуть зламати мережу.
"Стандартним" способом, яким ми керувалися в такому сценарії, було переоцінення: зробити зчитування зберігання більш дорогим для зменшення максимального значення на блок до безпечного рівня. Однак ми маємо вжезробленоцебагаторази, і це зробить занадто багато програм занадто дорогими, щоб робити це знову. Кращим підходом був би багатовимірний газ: обмеження та оплата окремо за доступ до сховища, тримаючи середнє використання на рівні 1,000 доступів до сховища на блок, але встановлюючи межу на блок, наприклад, 2,000.
Ще один ресурс, над яким варто подумати, - це збільшення розміру стану: операції, які збільшують розмір стану Ethereum, який повні вузли будуть зобов'язані утримувати з того часу. Унікальна властивість збільшення розміру стану полягає в тому, що обґрунтування обмеження цього процесу повністю випливає з тривалого сталого використання, а не зі сплесків. Тому може бути корисність у добавленні окремого газового параметра для операцій збільшення розміру стану (наприклад, зміна з нуля на ненуль у SSTORE, створення контракту), але з іншою метою: ми могли б встановити плаваючу ціну для досягнення певного середнього використання, але взагалі не встановлювати обмеження на кожний блок.
Це показує одну з потужних властивостей багатовимірного газу: це дозволяє нам окремо задавати питання (i) яке є ідеальним середнім використанням і (ii) яке є безпечним максимальним використанням на блок для кожного ресурсу. Замість того, щоб встановлювати ціни на газ на основі максимумів на блок та дозволяти середньому використанню слідувати за ним, ми маємо
2𝑛
ступені свободи для встановлення
2𝑛
параметри, налаштовуючи кожен з них на основі того, що є безпечним для мережі.
Більш складні ситуації, наприклад, коли два ресурси мають урахування безпеки, які частково додаються, можуть бути вирішені шляхом надання опкоду або вартості ресурсу певної кількості різних типів газу (наприклад, збереження від нуля до ненульового SSTORE може коштувати 5000 газу, який підтверджує бездоганність клієнта, та 20000 газу для розширення сховища).
Дозвольте
𝑥1
бути вартістю газу для даних та
𝑥2
бути вартістю газових обчислень, тому в одновимірній газовій системі ми можемо записати вартість газу для транзакції:
газ=х1*дата+х2*компутація
У цій схемі ми замість цього визначаємо вартість газу транзакції як:
газ=макс(х1*дата,х2*компутація)
Тобто замість оплати транзакції за дані плюс обчислення транзакція оплачується на основі того, яку з двох ресурсів вона використовує більше. Це легко можна розширити, щоб охопити більше вимірів (наприклад,
макс(…,х3*стораге_аксес)
)
Має бути легко бачити, як це покращує пропускну здатність, зберігаючи безпеку. Теоретично максимальна кількість даних в блоку все ще
газлімітх1
, точно так само, як і в одновимірній схемі газу. Так само, теоретична максимальна кількість обчислень є
ГАЗОВИЙ ЛІМІТx2
, знову точно так само, як і в одновимірній схемі газу. Однак вартість газу будь-якої транзакції, яка споживає як дані, так і обчислення, зменшується.
Це приблизно схема, що використовується в запропонованій EIP-7623, щоб зменшити максимальний розмір блоку, збільшуючи кількість блоків. Точний механізм в EIP-7623 трохи складніший: він зберігає поточну ціну calldata у розмірі 16 газу за байт, але додає «мінімальну ціну» у розмірі 48 газу за байт; транзакція сплачує більш високу ціну (16 bytes + виконавчий газ) та (48 байт). В результаті EIP-7623 зменшує теоретичний максимальний обсяг даних транзакції в блоку з приблизно 1,9 МБ до приблизно 0,6 МБ, залишаючи витрати більшості додатків без змін. Перевага такого підходу полягає в тому, що це дуже маленька зміна від поточної одновимірної схеми газу, тому вона дуже легко впроваджується.
Є два недоліки:
Я б сказав, що правило у стилі EIP-7623, як для даних виклику транзакції, так і для інших ресурсів, може принести достатньо великі переваги, щоб варто було це навіть попри ці недоліки. Однак, якщо і коли ми готові вкласти (значно більше) зусиль у розробку, існує більш ідеальний підхід.
Давайте спочатку підсумуємо, як працює «звичайний» EIP-1559. Ми зосередимося на версії, яка була введена в EIP-4844 для крапельок, оскільки математично вона більш елегантна.
Ми відстежуємо параметр, excess_blobs. Протягом кожного блоку ми встановлюємо:
excess_blobs <— макс(перебільшені_крапки + len(блок.крапки) - ЦІЛЬ, 0)
Де TARGET = 3. Це означає, що якщо блок має більше куль, ніж ціль, excess_blobs збільшується, а якщо блок має менше, ніж ціль, воно зменшується. Потім ми встановлюємо blob_basefee = exp(excess_blobs / 25.47), де exp є наближенням експоненційної функції
𝑒𝑥𝑝(𝑥)=2.71828𝑥
.
Це означає, що кожного разу, коли excess_blobs збільшується на ~25, базова комісія за краплу збільшується в ~2,7 раза. Якщо краплі стають занадто дорогими, середнє використання падає, і excess_blobs починає зменшуватися, автоматично знижуючи ціну знову. Ціна краплі постійно коригується, щоб переконатися, що у середньому блоки наполовину заповнені - тобто вони містять у середньому по 3 краплі кожна.
Якщо використання тимчасово зросте, то вступає в силу обмеження: кожний блок може містити максимум 6 кульок, і в такому випадку транзакції можуть конкурувати між собою, підвищуючи свої пріоритетні комісії. У звичайному випадку кожній кульці досить заплатити базову комісію для кульки плюс невелику додаткову пріоритетну комісію як стимул для включення взагалі.
Цей вид ціноутворення існував в Ethereum для газу протягом років: дуже схожий механізм було введено з @vbuterin/eip-1559-faq">EIP-1559 повернувся у 2020 році. З EIP-4844 у нас тепер є дві окремо плаваючі ціни на газ та на блоки.
Базова комісія за газ протягом однієї години на 2024-05-08, в гвей. Джерело: ultrasound.money.
В принципі ми можемо додати ще окремі плаваючі комісії за зчитування зберігання та інші види операцій, хоча з одним застереженням, про яке я розширю в наступному розділі.
Для користувачів досвід дуже схожий на сьогодні: замість оплати однієї базової плати ви платите дві базові плати, але ваш гаманець може абстрагувати це від вас і показати лише очікувану плату та максимальну плату, яку ви можете очікувати.
Для будівельників блоків, в більшості випадків оптимальна стратегія та сама, що й сьогодні: включати все, що є дійсним. Більшість блоків не є повними - ні один в газінів крапляхОдним з викликів є випадок, коли є достатньо газу або достатньо крапель, щоб перевищити обмеження блоку, і забудовник повинен потенційно вирішити Багатовимірна задача про рюкзакщоб максимізувати свій прибуток. Однак навіть там існують досить хороші апроксимаційні алгоритми, і вигоди від створення власних алгоритмів для оптимізації прибутку в цьому випадку набагато менші, ніж вигоди від того самого зроблення з МЕV.
Для розробників основним викликом є потреба переконструювати можливості EVM та його навколишню інфраструктуру, які сьогодні розроблені навколо однієї ціни та одного ліміту, в проект, що враховує кілька цін та кілька лімітів. Одна з проблем для розробників додатків полягає в тому, що оптимізація стає трохи складнішою: у деяких випадках ви вже не зможете однозначно сказати, що A ефективніший за B, оскільки якщо A використовує більше calldata, але B використовує більше виконання, то A може бути дешевшим, коли calldata дешевий, і дорожчим, коли calldata дорогий. Однак розробники все ще зможуть отримати досить гарні результати, оптимізуючи на основі середньострокових історичних середніх цін.
Є одна проблема, яка не виникла з блобами, і не виникне з EIP-7623 або навіть з «повною» багатовимірною реалізацією ціноутворення для calldata, але виникне, якщо ми спробуємо окремо встановити ціну на доступ до стану або будь-які інші ресурси: обмеження газу в підвикликах.
Ліміти газу в EVM існують в двох місцях. По-перше, кожна транзакція встановлює ліміт газу, який обмежує загальну кількість газу, який може бути використаний у цій транзакції. По-друге, коли контракт викликає інший контракт, виклик може встановити свій власний ліміт газу. Це дозволяє контрактам викликати інші контракти, яким вони не довіряють, і все ж гарантувати, що у них залишиться газ для виконання інших обчислень після цього виклику.
Слід угоди з абстракцією рахунку, де рахунок викликає інший рахунок, і надає викликачеві обмежену кількість газу, щоб забезпечити можливість зовнішнього виклику продовжувати працювати навіть якщо викликач витрачає весь газ, який був виділений для нього.
Виклик полягає в тому, щоб зробити газ багатовимірним між різними типами виконання, здається, що це потребуватиме підвикликів для надання кількох обмежень для кожного типу газу, що потребуватиме дуже глибоких змін у EVM і не буде сумісним з існуючими додатками.
Це одна з причин, чому багатовимірні пропозиції щодо газу часто зупиняються на двох вимірах: даних та виконанні. Дані (чи то дані виклику транзакції, чи блоби) призначені поза EVM, тому нічого всередині EVM не потрібно змінювати, щоб окремо цінувати дані виклику транзакції або блоби.
Ми можемо подумати про «рішення у стилі EIP-7623» для цієї проблеми. Ось одна проста реалізація: під час виконання збільште вартість операцій зберігання в 4 рази; для спрощення аналізу скажемо, що 10000 газу на операцію зберігання. В кінці транзакції повертати min(7500 * операції_зберігання, виконання_газу). Результат буде таким, що після відрахування повернення користувач оплачує:
execution_газ + 10000 операції зберігання - мін(7500 операції зберігання, виконання газу)
Яке дорівнює:
макс (виконання_газ + 2500 операції зберігання, 10000storage_operations)
Це віддзеркалює структуру EIP-7623. Інший спосіб зробити це - відстежувати storage_operations та execution_gas в реальному часі, та стягувати 2500 або 10000, залежно від того, скільки максимально (execution_gas + 2500)операції зберігання, 10000операції зберігання) збільшується в момент виклику опкоду. Це уникне потреби в транзакціях перерозподілити газ, який вони в основному отримають назад у вигляді відшкодувань.
Ми не отримуємо деталізованого дозволу для підвикликів: підвиклик може використати всю "дозволену кількість" угоди для дешевих операцій зберігання. Але ми отримуємо щось достатньо добре, де контракт, який робить підвиклик, може встановити обмеження та забезпечити, що після завершення виконання підвиклику у головного виклику все ще є достатньо газу для виконання будь-якої післяопрацювання, яке потрібно зробити.
Найпростіше «повне багатовимірне рішення ціноутворення», яке я можу уявити, полягає в тому, що ми розглядаємо ліміти викликів підпрограм як пропорційні. Тобто, припустимо, що є
𝑘
різні типи виконання, і кожна транзакція встановлює багатовимірний ліміт
𝐿1…𝐿𝑘
. Припустимо, що в даний момент виконання залишок газу становить
𝑔1…𝑔𝑘
Припустимо, що виклик opcode викликається з лімітом газу для підвиклику.
𝑆
. Дозвольте
𝑠1=𝑆
, а потім
𝑠2=𝑠1газ1∗газ2
,
𝑠3=𝑠1газ1∗газ3
, і так далі.
Це означає, що ми розглядаємо перший тип газу (реалістично, виконання VM) як певний вид привілейованого «одиниці обліку», а потім призначаємо інші типи газу так, щоб підвиклик отримував той самий відсоток доступного газу по кожному типу. Це трохи некрасиво, але це максимізує зворотню сумісність. Якщо ми хочемо зробити схему більш «нейтральною» між різними типами газу, за рахунок жертвування зворотною сумісністю, ми можемо просто зробити параметр обмеження газу підвиклика представляти дріб (наприклад, [1…63] / 64) від залишкового газу в поточному контексті).
У будь-якому випадку, проте, варто підкреслити, що після початку використання багатовимірного виконавчого газу рівень внутрішньої потворності збільшується, і це, здається, важко уникнути. Таким чином, наше завдання - зробити складений компроміс: чи ми приймаємо трохи більше потворності на рівні EVM, щоб безпечно розблокувати значні переваги масштабованості L1, і, якщо так, який саме конкретний пропозиція працює найкраще для економіки протоколу та розробників додатків? Дуже ймовірно, це не одна з тих, про які я згадував вище, і все ще є можливість придумати щось більш елегантне та краще.