Можливо, найважливіша подія в історії Ethereum, The Merge, відбулася 15 вересня 2022 року. Вона ознаменувала перехід мережі від до , докорінно змінивши спосіб досягнення консенсусу в Ethereum. Але чому це називається « », а не «перехід»? Proof of Work Proof of Stake The Merge До The Merge Ethereum працювала на консенсусі Proof of Work, механізмі, який вимагав від «майнерів» розв’язувати складні криптографічні головоломки для підтвердження транзакцій та створення нових блоків. PoW — це круто, але він має багато обмежень, таких як використання величезної обчислювальної потужності, що призводить до високого споживання електроенергії та екологічних проблем, низька пропускна здатність транзакцій через повільний час перевірки, що є викликом для інституційного впровадження, вищий ризик централізації, оскільки він може консолідуватися до кількох потужних майнінгових компаній тощо. Ці та інші причини стали мотивацією, яка спонукала Ethereum Foundation, лише через три роки після заснування, почати розробку нового консенсусу під назвою Proof of Stake, покликаного вирішити більшість проблем, які постали перед PoW. 1 грудня 2020 року Ethereum запустила свою першу версію PoS, новий ланцюг під назвою Beacon Chain. Beacon Chain не обробляла користувацькі транзакції. Її єдиною метою було координація валідаторів та досягнення консенсусу за допомогою нового механізму під назвою Gasper. Транзакції все ще оброблялися в основному ланцюзі Proof of Work, тому обидва ланцюги, основний ланцюг Ethereum та Beacon Chain, працювали паралельно. Майже два роки ці два ланцюги працювали незалежно. Потім, 15 вересня 2022 року, оригінальний ланцюг відмовився від свого консенсусу на основі майнінгу та підключився безпосередньо до Beacon Chain. Таким чином, два ланцюги стали одним. Ось чому це називається The Merge, а не перехід. Сьогодні Ethereum працює як двошаровий блокчейн. Шар консенсусу, яким раніше була Beacon Chain, відповідає за пропозиції блоків, підтвердження та фіналізацію. Шар виконання, оригінальний ланцюг Ethereum, відповідає за обробку транзакцій. Можливо, ви чули про них як про Eth2 та Eth1 відповідно, але Ethereum Foundation відмовилася від цього найменування, оскільки воно передбачало два окремі мережі, а не два шари однієї системи. Ця стаття зосереджується на шарі консенсусу. Зокрема, як Beacon Chain функціонує як машина станів. Що таке BeaconState? Щоб зрозуміти, як відбуваються переходи станів машини станів, спочатку потрібно зрозуміти, що фактично містить стан на момент генерації ланцюга. Стан Beacon Chain представлений одним об’єктом під назвою BeaconState. Він містить все, що потрібно шару консенсусу для роботи. Іноді його називають «об’єктом Бога». Сама специфікація групує поля за призначенням, що є найчіткішим способом їх розглянути. class BeaconState(Container): genesis_time: uint64 genesis_validators_root: Root slot: Slot fork: Fork latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] eth1_deposit_index: uint64 validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] previous_justified_checkpoint: Checkpoint current_justified_checkpoint: Checkpoint finalized_checkpoint: Checkpoint Версіонування: genesis_time: uint64 genesis_validators_root: Root slot: Slot fork: Fork Перші чотири змінні відповідають на запитання «який це ланцюг і де ми в ньому?». Ці поля закріплюють ідентичність ланцюга та повідомляють кожному вузлу, яких правил протоколу слід дотримуватися. Час генезису — це Unix-мітка часу, яка встановлюється на початку ланцюга і ніколи не змінюється. Час генезису Beacon Chain — 1606824023, що відповідає 1 грудня 2020 року, 12:00:23 UTC. Якщо ви коли-небудь запитували «block.timestamp» з смарт-контракту, це значення розраховується з цього поля. Кореневий елемент валідатора генезису, як і мітка часу, також був доданий на початку ланцюга. Він фактично діє як роздільник домену; він змішується з підписом валідатора під час пропозицій блоків та підтверджень, щоб відрізнити Ethereum Mainnet від будь-якого іншого ланцюга. Slot — це просто лічильник, який показує, де знаходиться ланцюг у часі. Він збільшується кожні 12 секунд, незалежно від того, чи було вироблено блок. Тоді як Fork — це об’єкт, що містить три поля: попередню версію ланцюга, поточну версію ланцюга та епоху. Коли відбулося перше оновлення Beacon Chain 27 жовтня 2021 року, версії змінилися з Phase 0 на Altair. Поточна версія на момент написання цієї статті — Fulu, а попередня — Electra. Як і кореневий елемент валідатора, хеш версії додається до підписів, щоб відрізнити одну версію форку від іншої. Epoch, з іншого боку, — це пакет із 32 слотів, тобто , приблизно 6,4 хвилини. Саме тут відбуваються перевірки фінальності, покарання за слешинг, черга виходу та всі інші цікаві специфічні для консенсусу речі. Саме тут працює Casper FFG, остання частина Gasper. 12 сек x 32 Історія latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] Цей розділ відповідає на запитання: «Що сталося з цим ланцюгом?». Ці змінні надають ланцюгу стислу пам’ять про його минуле, дозволяючи валідаторам посилатися на попередні стани та перевіряти їх, не зберігаючи все. Заголовок останнього блоку зберігає заголовок найостанніше обробленого блоку. Він використовується для запобігання дублюванню блоків, оскільки перед обробкою нового блоку ланцюг перевіряє, чи відповідає кореневий елемент батьківського блоку кореневому елементу останнього блоку. Поля кореневих елементів блоків та кореневих елементів стану — це списки, які зберігають минулі кореневі елементи блоків та станів відповідно, поки не будуть заповнені. У кожному слоті кореневі елементи записуються до відповідних масивів за індексом . Це дозволяє ланцюгу перевірити, як виглядав стан у будь-якому нещодавньому слоті протягом 27-годинного вікна. Historical root додає об’єднаний хеш масиву кореневих елементів блоків та станів, коли вони заповнюються. Список не має обмежень, але він зростає повільно, лише один запис кожні 27 годин. slot%8192 Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] eth1_deposit_index: uint64 До The Merge Eth2 (Beacon Chain) потрібно було відстежувати, що відбувається в Eth1 (PoW ланцюг), зокрема транзакції депозитів, де нові валідатори блокували 32 ETH. Можливо, ви дивуєтеся, чому 32 ETH, призначені для PoS, були заблоковані в PoW ланцюзі, а не в Beacon Chain. Відповідь проста: тому що сама Beacon Chain не мала можливості передавати токени або обробляти транзакції, оскільки вона не могла обробляти депозити токенів нативно. Дані Eth1 містять три підполя: , який є кореневим елементом мерклівського дерева контракту депозитів; , загальна кількість депозитів, зроблених до контракту; та , хеш відповідного блоку eth1. deposit root deposit count block hash Beacon Chain не може просто довіряти погляду одного валідатора на Eth1 ланцюг, оскільки різні валідатори можуть бачити різні стани через затримки мережі, тому вона використовує механізм голосування, який дозволяє пропонентам блоків включати свій погляд на поточні дані Eth1 у своїх блоках. Ці голоси накопичуються в цьому списку протягом періоду голосування, і якщо будь-яке значення отримає більше половини голосів протягом цього періоду, воно стає новими даними Eth1. Наприкінці періоду голосування список очищається, і голосування починається знову. Eth Deposit Index відстежує, скільки депозитів з контракту депозитів було оброблено до цього моменту. Коли ланцюг обробляє новий блок, він перевіряє, чи є необроблені депозити, порівнюючи цей індекс із полем кількості депозитів у даних Eth1. Якщо кількість депозитів вища, блок повинен включати наступні депозити до максимальної кількості депозитів за блок, яка на той час становила 16. Реєстр validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] Ця змінна, по суті, зберігає список тих, хто бере участь у консенсусі, та їхній стек. Цікавий факт про поле валідаторів полягає в тому, що воно лише зростає і ніколи не зменшується, навіть після виведення коштів валідатором, його запис залишається у списку. Наразі існує 2 210 484 записів, з яких лише 962 941 активні. Поле Validator має вісім підполів: , яке, по суті, є публічним ключем валідатора; , куди надходить їхній стек при виведенні; , їхній баланс, округлений до найближчого gwei, який використовується для розрахунку винагород та штрафів, і він оновлюється лише на межах епох з гістерезисом, щоб запобігти його мерехтінню вгору та вниз; , булевий прапорець, що позначає, чи був валідатор слешнутий; , номер епохи, коли валідатор став придатним для активації; , епоха, коли він став активованим; , епоха, коли він вийшов; і, нарешті, , епоха, коли його баланс може бути виведений. pubKey withdrawable credentials effective balance slashed activation eligibility epoch activation epoch exit epoch withdrawable epoch Щоб пояснити, чому ми маємо поле effective balance у списку валідаторів, а поле balances безпосередньо в стані beacon, полягає в тому, що поле effective balance не оновлюється в момент, коли оновлюються ваші фактичні баланси; існує буфер, щоб запобігти його руху вперед і назад. Без гістерезису, валідатор, що коливається навколо 32 ETH, скажімо, коливається між 31.99 і 32.01 щоепохи через винагороди та штрафи, матиме своє effective balance, що перемикається між 31 і 32 щоепохи. Це означало б постійне пере Мерклізовування об’єкта валідатора та зміну його ваги в розрахунках комітету в кожній епосі. Випадковість randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] randao_mixes — це список фіксованого розміру 65 536 (близько 2^16) записів. Щоразу, коли валідатор пропонує блок, він додає до списку те, що ми називаємо «randao reveal». Це, по суті, поточний номер епохи, підписаний валідатором. Після підписання ланцюг бере його і застосовує операцію XOR з останнім міксом для поточної епохи, що створює новий мікс. Усі пропоненти в епосі роблять те саме, щоб отримати остаточний накопичений мікс для наступної епохи. Мікс Randao використовується для визначення комітету та пропонентів блоків для наступної епохи. Комітет, що складається з усіх активних валідаторів, розділених на 32 слоти, визначається алгоритмом перемішування «swap-or-not». Цей алгоритм, по суті, просто випадково переставляє індекси валідаторів за допомогою міксу. Для вибору пропонента блоку ланцюг хешує мікс Randao, щоб сформувати насіння. Потім він проходить через усіх активних валідаторів, починаючи з випадкового зсуву, отриманого з цього насіння. Для кожного кандидата він перевіряє, чи хеш насіння та індекс валідатора, поділений на ефективний баланс валідатора, перевищує поріг. Якщо так, валідатор стає пропонентом, якщо ні, він пропускає наступного. На практиці це швидко знаходить, оскільки більшість активних валідаторів мають баланс 32 ETH. Слешинг slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] Валідатор слешиться з двох причин: пропонування двох різних блоків для одного слоту, намагаючись створити форк, або створення суперечливих підтверджень. Поле слешингу — це список фіксованого розміру 8192 записи, по одному на епоху. Воно містить суму всіх ефективних балансів валідаторів, які були слешнуті. Це поле використовується для розрахунку суми штрафу. Підтвердження previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] Кожен активний валідатор робить одне підтвердження за епоху, і саме ці підтвердження керують як правилом вибору форку (LMD-GHOST), так і механізмом фіналізації (Casper FFG). Підтвердження містить шість підполів: , для якого валідатор робить підтвердження; — це блок, який валідатор вважає головою ланцюга, він вважається голосом LMD-GHOST валідатора, що використовується для визначення вибору форку; — це чекпойнт епохи, який, на думку валідатора, є обґрунтованим, а — це поточна епоха, для якої валідатор робить підтвердження, обидва разом утворюють голос Casper FFG, який використовується для фіналізації. Простіше кажучи, валідатор підтверджує, що вихідна епоха повинна бути фіналізована, а цільова епоха повинна бути обґрунтована. вказує, який валідатор у комітеті підтвердив. Оскільки дешевше комбінувати речі як біти в байті, біт агрегації зберігається в бітовому полі для ефективності пам’яті. Нарешті, ми маємо валідатора щодо даних підтвердження. slot beacon block root source target aggregation bits signature Фінальність justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] previous_justified_checkpoint: Checkpoint current_justified_checkpoint: Checkpoint finalized_checkpoint: Checkpoint Фінальність означає, що блок і всі його транзакції ніколи не можуть бути скасовані. У Beacon Chain фінальність є абсолютною. Як тільки епоха фіналізована, єдиний спосіб її скасувати — це якщо 1/3 всього стейкінгового ETH буде слешнуто, що коштуватиме мільярди доларів. Justification bits — це бітовий вектор довжиною 4, який, по суті, просто відстежує, чи були обґрунтовані останні чотири епохи. Попередній обґрунтований чекпойнт — це чекпойнт, який був обґрунтований на момент попередньої епохи. Поточний обґрунтований чекпойнт — це найостанніший обґрунтований чекпойнт, тоді як finalized checkpoint — це найостанніший фіналізований чекпойнт. Епоха стає обґрунтованою, коли 2/3 загального активного стеку підтверджують супермажоритарний зв’язок, що вказує на цю епоху як цільову. Фіналізація відбувається, коли ви отримуєте два послідовних обґрунтованих чекпойнта. В момент, коли другий стає обґрунтованим, перший підвищується до фіналізованого, хоча є деякі нюанси. Машина станів Тепер, коли ми знаємо, що містить стан, ми можемо розглянути, як він змінюється. Кожні 12 секунд настає новий слот. Якщо блок запропоновано, функція переходу стану бере поточний стан і цей блок, виконує перевірку, оновлює та видає новий стан. Цей процес розділений на три етапи згідно зі специфікацією: обробка слота, обробка блоку та обробка епохи. Обробка слота Обробка слота відбувається щоразу, коли ланцюг повинен перейти від одного слота до наступного, незалежно від того, чи був вироблений блок. Три речі відбуваються, коли слот переходить від N до N+1. По-перше, кореневий елемент стану для слота N оновлюється, щоб зберегти запис про те, яким був цей слот у цей момент. По-друге, заголовок останнього блоку, який зберігає заголовок найостанніше обробленого блоку, також оновлюється, пам’ятайте, що це поле використовується для запобігання дублюванню блоків. Також кореневий елемент цього завершеного заголовка записується до кореневого елемента блоку. Нарешті, лічильник слотів збільшується на одиницю. Можливо, ви запитуєте, що відбувається зі станом, коли пропонент пропускає свій слот. Ну, всі стани все одно оновлюються, кореневі елементи блоків цього слоту, наприклад, міститимуть кореневий елемент того самого останнього заголовка блоку, оскільки новий блок не надійшов, щоб замінити його. Після збільшення лічильника слотів ланцюг перевіряє, чи перетнув він межу епохи (це можна легко зробити за допомогою ). Якщо так, то обробка епохи запускається перед будь-чим іншим. Отже, технічно, кожні 32 слоти обробка епохи відбувається одночасно з обробкою слота, тобто обробка епохи відбувається після переходу слота, але до обробки блоку для цього слота. slot mod 32 == 0 Останній факт, який варто зазначити: коли ми досягаємо позиції, коли масиви кореневих елементів стану та блоків заповнені (тобто ), перш ніж обидва масиви почнуть перезаписуватися новими даними, оскільки вони виглядають як колові, ланцюг хешує два поля разом і додає їх до історичного стану. slot mod 8192 == 0 Обробка блоку Обробка блоку відбувається, коли для слота фактично запропоновано блок. Після того, як обробка слота переводить стан до правильного слота, обробка блоку бере підписаний блок і застосовує його вміст до стану. Вона має дві основні частини: перевірка заголовка блоку та обробка тіла блоку. Перш за все, ланцюг перевіряє кілька моментів, наприклад, чи відповідає заголовок блоку поточному слоту стану, і чи є індекс пропонента блоку фактично валідатором, вибраним через randao. Нарешті, він перевіряє, чи відповідає кореневий елемент батьківського блоку кореневому елементу останнього блоку. Якщо перевірка успішна, заголовок блоку зберігається як останній заголовок блоку в стані. Далі, пропонент повинен включити randao reveal, який, як я вже згадував, по суті, є поточним номером епохи, підписаним валідатором. Ланцюг перевіряє підпис за публічним ключем пропонента. Легко зрозуміти, що цей поточний метод є детермінованим, тобто підпис валідатора завжди буде однаковим для одного номера епохи, це правда, насправді весь сенс такого підходу полягає в тому, щоб дозволити будь-кому використовувати публічний ключ валідатора для перевірки номера епохи, який валідатор фактично підписав. Зверніть увагу, що пропонент не може пропустити randao reveal; якщо він пропонує блок, він повинен включити його, єдиний спосіб пропустити процес — це взагалі не пропонувати блок. Після цього пропонент також включить своє бачення стану контракту депозитів Eth1 ланцюга як голос даних Eth1. Якщо будь-яке значення даних Eth1 у списку голосів досягає більшості, воно стає новими даними Eth1 у стані. Під час обробки блоку поле пропонента слашингу містить докази того, що валідатор підписав два різні заголовки блоків для одного слоту; ланцюг перевіряє обидва підписи, і якщо вони дійсні, він слешить їх, спочатку встановлюючи їхній прапорець slashed на true, а потім додаючи їхній ефективний баланс до масиву slashings. Також, для атестаторів, якщо є будь-яке суперечливе підтвердження, ланцюг перевіряє його та ідентифікує валідаторів-винуватців за їхніми підписами, а потім слешить цих валідаторів тим самим способом та процесом, що й слешинг пропонента блоку. Нові підтвердження в блоці перевіряються, підтвердження перетворюється на очікуюче підтвердження шляхом додавання двох нових полів: inclusion delay та proposer index, а потім воно додається до підтверджень поточної або попередньої епохи. Після додавання нічого особливого не відбувається, оскільки вони не впливають на стан негайно; вони залишаються там доки не будуть оцінені під час обробки епохи. Нові депозити валідаторів з контракту депозитів Eth1 обробляються; блок повинен включати всі очікуючі депозити до максимального значення 16. Ланцюг перевіряє доказ мерклівського дерева кожного депозиту щодо кореневого елемента депозиту, щоб забезпечити його правильність. Якщо публічний ключ депонента новий, додається новий запис валідатора, а також відповідний баланс. Валідатор може сигналізувати про своє бажання вийти, надіславши підписаний добровільний вихід. Ланцюг перевіряє, чи був валідатор активним щонайменше 256 епох, і чи поточна епоха щонайменше відповідає зазначеній епосі виходу. Якщо все гаразд, встановлюються епоха виходу та епоха виведення коштів валідатора. Після обробки всього вищезазначеного залишається ще один важливий крок: обчислений кореневий елемент стану іншим вузлом порівнюється з кореневим елементом стану, включеним до блоку. Якщо вони не збігаються, весь блок відхиляється. Обробка епохи Обробка епохи запускається на межі епохи, тобто кожний крок . Вона відбувається під час обробки слота, коли лічильник слотів переходить до нової епохи. Це найскладніший етап, оскільки тут відбувається багато цікавих речей, пов’язаних з консенсусом. slot mod 32 = 0 По-перше, запускається обґрунтування та фіналізація; саме тут працює Casper FFG. Процес може здатися складним, але він дуже простий. Простіше кажучи, ланцюг розглядає підтвердження з попередньої епохи та підраховує ефективні баланси валідаторів, які підтвердили з правильним цільовим показником. Якщо ця сума становить щонайменше 2/3 загального активного балансу, цільова епоха стає обґрунтованою, і якщо дві послідовні епохи обґрунтовані, раніша стає фіналізованою. Просто! Один факт, який я не згадав на етапі обробки блоку: на кожному етапі валідатори накопичують винагороди; вони отримують винагороду за включення підтвердження, за додавання доказів слешингу або навіть за звичайну базову винагороду. Ці накопичені винагороди оцінюються ланцюгом за попередню епоху на етапі обробки епохи, і їхній баланс відповідно коригується. Якщо ланцюг не фіналізувався протягом більше чотирьох епох, вмикається те, що називається «inactivity leak». На додаток до звичайних штрафів за пропущені підтвердження, неактивні валідатори отримують додатковий штраф за неактивність, який квадратично зростає з кожною епохою з моменту останньої фіналізованої епохи. Іншими словами, чим довше блок не фіналізується, тим суворіший ваш штраф. Можливо, ви запитуєте, навіщо це потрібно. Тепер, коли блок не фіналізувався протягом деякого часу, це означає, що немає більшості голосів за цей блок, а оскільки голосування за фіналізацію вимірюється вагою ефективного балансу валідатора, тобто скільки ETH має валідатор, зменшення його ефективного балансу зменшує його голосуючу силу. Таким чином, Ethereum використовує це як спосіб примусити до фіналізації блоків. Варто зазначити, що під час inactivity leak навіть валідатори, які роблять правильні підтвердження, не отримують винагород. Все переходить у режим чистих штрафів, щоб прискорити перебалансування. Ще одна важлива подія, яка відбувається на цьому етапі, — це активація валідаторів, чий епоха активації досягнуто. Також валідатори, чия епоха виходу настала, вилучаються з активного набору валідаторів. Далі, пам’ятайте, що ефективний баланс не оновлюється з кожним слотом та епохою, оскільки застосовується гістерезис. Він оновлюється лише тоді, коли фактичний баланс значно перевищує поточний поріг верхнього рівня ефективного балансу; ефективний баланс збільшується на 1 ETH, і якщо він падає нижче порогового значення нижнього рівня, він зменшується на 1 ETH. Нарешті, мікс randao поточної епохи копіюється до слоту наступної епохи. Як видно з усього вищесказаного, обробка епохи є найскладнішою частиною машини станів і вимагає значних обчислень, тому вона завжди призводить до великої роботи з оптимізації для інженерів, які створюють клієнти ланцюга. Від Phase 0 до Fulu BeaconState, який ми розглянули досі, — це версія Phase 0, оригінальна. Але Beacon Chain — це жива система. Кожен форк рівня консенсусу модифікував BeaconState, або додаючи нові поля, видаляючи старі, або просто змінюючи спосіб роботи деяких існуючих. Наприклад, у форку Altair списки підтверджень попередньої та поточної епох були повністю видалені, щоб замінити їх на попередньої та поточної епох. Різниця полягає в тому, що якщо перший зберігав повні об’єкти підтверджень, новий тип participation зберігає їх лише у бітовому форматі. Тепер кожен валідатор отримує три біти на епоху, що вказують, чи правильно він визначив джерело, ціль та голову. Це призвело до різкого зменшення розміру пам’яті. Bellatrix, форк Merge, ввів поле execution payload до beaconState; це поле використовується для з’єднання шару консенсусу та шару виконання. Поля Eth1 були збережені, але їхня роль зменшилася, оскільки вони більше не були потрібні. participation Форк Capella запровадив можливість виведення коштів для валідатора; він записує це, додаючи нове поле під назвою next withdrawal index до beaconState. Historical roots було замінено на historical summaries, які зберігають кореневий елемент блоку та кореневий елемент стану в структурі, замість їх хешування разом. Deneb fork, з іншого боку, майже не вплинув на beacon