---
# System prepended metadata

title: стейт-финализация (план имплементации)

---

# стейт-финализация (план имплементации)


## Intro

Изначально, блок создаваемый криейтором, является контейнером транзакций и стейт для него не вычисляется.
Хэш блока высчитывается по "базовому набору полей" (определенных в момент создания блока) и используется как идентификатор блока.

> Базовые поля: 	
ParentHashes
Slot
Height
Coinbase
Time
Extra
TxHash
GasLimit
LFNumber - номер последнего финализированного блока на момент создания 
LFHash - стейт-хэш последнего финализированного блока на момент создания


В процессе финализации вычисляется стейт блока и определяются "поля состояния" и сохраняются в заголовок блока

> Поля состояния
BaseFee
ReceiptHash
Number
GasUsed
Bloom
Root

Стейт-хэш - результат хэширования всех полей заголовка, используется для контроля целостности цепи и валидации пропагейт-блоков. Стейт-хэш последнего финализированного блока сетится в новые блоки при создании.



## Implementation

### Block structure 

1. ~~Удалить поля~~
	````
    MixDigest      common.Hash      `json:"mixHash"`
	FinNumber      *uint64          `json:"finNumber"        gencodec:"required"`
	FinRoot        common.Hash      `json:"finStateRoot"     gencodec:"required"`
	FinReceiptHash common.Hash      `json:"finReceiptsRoot"  gencodec:"required"`

    ````
1. ~~Добавить новые поля в заголовок блока~~
    ````
	LFNumber      uint64           `json:"lfNumber"        gencodec:"required"`
	LFHash        common.Hash       `json:"lfHash"     gencodec:"required"`
    ````
1. ~~Исправить маршалинг в соответствии с изменениями~~ `/home/mezin/go/src/gwat/core/types/gen_header_json.go`
1. ~~Исправить КРУД в соответствии с изменениями~~ `core/rawdb/accessors_chain.go`
    - Number - не сохраняем в хидер, используем текущую логику (связано с обеспечением доступа к блокам во фризере).
    - Обратьть внимание, что везде где вызываются функции записи и удаления Number, скорее всего должно происходить запись удаление стейт-полей соответственно. Оттрекать и проанализировать вызовы `func WriteFinalizedHashNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64)` и `func DeleteFinalizedHashNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64)`, добавить подзадачи. 
1. ~~Пройтись по коду проверить вспомогательные структуры типа~~ `type headerMarshaling struct`, 
1. ~~Проверить в апи, реализации протоколов, и js(web3.js, перегенерировать бинд-код)~~

Итоговая структура блока:
````go
type Header struct {
	ParentHashes   common.HashArray `json:"parentHashes"     gencodec:"required"`
	Slot           uint64           `json:"slot"             gencodec:"required"`
	Height         uint64           `json:"height"           gencodec:"required"`
	LFNumber       *uint64          `json:"lfNumber"        gencodec:"required"`
	LFHash      common.Hash      `json:"lfStateRoot"     gencodec:"required"`
        Coinbase       common.Address   `json:"miner"            gencodec:"required"`
        TxHash         common.Hash      `json:"transactionsRoot" gencodec:"required"`
	GasLimit       uint64           `json:"gasLimit"         gencodec:"required"`
   	Time           uint64           `json:"timestamp"        gencodec:"required"`
	Extra          []byte           `json:"extraData"        gencodec:"required"`
	// BaseFee was added by EIP-1559 and is ignored in legacy headers.
	BaseFee     *big.Int     `json:"baseFeePerGas" rlp:"optional"`
        Bloom       Bloom        `json:"logsBloom"     rlp:"optional"`
	GasUsed     uint64       `json:"gasUsed"       rlp:"optional"`
	Root        common.Hash  `json:"stateRoot"     rlp:"optional"`
	ReceiptHash common.Hash  `json:"receiptsRoot"  rlp:"optional"`
        // not saving to header due to supporting access to block in freezer (ro-db)
        Number      *uint64      `json:"number"        rlp:"optional"`
}

````


### Block functionality 

1. ~~Исправить/добавить/удалить функционал Хидера/Блока соответствующий изменениям структуры~~ 
1. ~~Функция хэша `func (h *Header) Hash() common.Hash` - сетить нуль-значения для всех стейт полей по  аналогии с Number~~
2. ~~Добавить блоку и хидеру функцию  `func (h *Header) FinalizedHash() common.Hash`, которая будет высчитывать хэш с учетом и стейт-полей. Если блок не финализирован - возвращает `common.Hash{}`.~~
3. Создание нового блока `NewBlock` уделить особое внимание



### Block creation

1. ~~__ParentHashes__ - при установке использовать сортировку asc~~
1. ~~__LFNumber__ - номер последнего финализированного блока~~
1. ~~__LFHash__ - вызов `FinalizedHash()` последнего финализированного блока~~
1. При добавлении транзакций высчитывать для каждой тх кол-во требуемого газа и добавлять в общую сумму `cumutativeGas`, используя функции
    - `IntrinsicGas`
    - `RunPrecompiledContract` (тут надо посмотреть насколько это затратно по ресурсам и времени) в качестве параметра `suppliedGas` использовать разницу `GasLimit - cumutativeGas` 
    - Если газ закончился - больше не добавляем тх
    - логика аналогична расчету `GasUsed`
1. Убрать весь функционал вычисления стейта (выбор стейта, рекоммит транзакций, силинг, етц.)
1. Реализовать функцию сохранения блока без стейта (взять текущую и выпилить все что связанно со стейтом)



### Block propagation
1. Новый функционал валидации
    1. ~~мог ли валидатор создавать этот блок~~
    2. ~~мог ли валидатор включить эти транзакции в этот блок (назначение адресов)~~
    3. ~~проверка по газ-лимиту(под вопросом)~~ 
    4. проверка правильности значений LFNumber и LFHash (берм блок по LFNumber, высчитываем `FinalizedHash()` - должен быть равен LFHash)
    5. ~~Проверяем наличие парентов~~ 
        - если нет - запросить у подключенных нод (запуск синхронизации) и запустить добавление блока по новой; 
        - если есть - проверить спайн-сортировку парентов 
    6. ~~Проверка парентов~~
        - ParentHashes не может быть пустым 
        - слоты парентов должны быть меньше слота блока
        - Высоты парентов должны быть меньше высоты блока (возможно реализовать точную проверку высоты)
        - проверка "parent-ancestor" парентов (G{slot:0}<-A{slot:1, parents:[G]}<-B{slot:2, parents:[**G**A]} : **G** - parent-ancestor, т.е. ссылка на блок, который уже есть в прошлом парентов)
    7. __Проверка нонсов транзакций__ (надо продумать)
1. Если блок невалидный - отбросить. Отбросить так, чтобы сеть и нода не завалились и продолжили работу в штатном режиме (проанализировать поведение этериума в подобных ситуацих, предложить решение) 
1. Убрать весь функционал вычисления стейта (выбор стейта, рекоммит транзакций, силинг, етц.)
1. Реализовать функцию сохранения блока без стейта (скорее всего таже самая что и при [создании блока](https://hackmd.io/hk6NZnAsS-yRudunJpqKtA?both#Block-creation))


### Block finalization
1. Проводим вычисление стейта для каждого блока в порядке финализации
    - за основу используем стейт блока в LFNumber 
    - накатываем блоки в порядке финализации на базе спайнов
    - после вычисления стейта сетим стейт-поля и сохраняем блок

### Misc
1. ~~Сейчас неправильно выполняется расчетет значений `GasUsed` и соответствующих значений в ресипте. Скорее всего из-за сквозного расчета при рекоммите транзакций. Проверить/исправить это при расчете стейта (накатывание тх и создание ресиптов)~~.
2. Убрать рекурсивные вызовы построения дерева предков при штатном режиме работы.
    - ~~везде где создается новый BlockDag (криейт, пропагейт, синхронизация дага) проверить возможность корректного заполнения `DagChainHashes`, т.е. наличие всех предков до финализированного блока (достигается конкатенацией и юником этих полей у парентов + фильтрация нефинализированных блоков)~~
    - ~~изучить все места обновления `BlockDag` (`ReviseTips()`) вполне возможно, что нам это в большинстве случаев не нужно.~~
    - ~~Наверное в `BlockDag.LastFinalizedHash` и `BlockDag.LastFinalizedHeight` можем сетить значения `LFHash` и `LFNumber` соответственно, и в течении всего времени они не меняются и использование построения графа предков излишне~~
    - оставить использование рекурсивного функционала при загрузке ноды, при обнаружении незагруженных предков и прочих особых случаех, где это оправдано.
    - Построение цепочек спайн-финализации тоже кажется можно реализовать на базе `DagChainHashes`

3. ~~Переделать метод `syncWithPeerUnknownDagBlocks` синхронизация только даг части при одинаковой финализации. Там старая схема ордеринга и построения типсов, сделать по аналогии с `syncWithPeerDagChain`, но надо быть внимательным, могут быть "подводные камни"~~ 
