---
# System prepended metadata

title: Синхронизация gwat-beacon network

---

# Синхронизация gwat-beacon network

## Intro

Решаемая задача сделать синхронизуцию узла связки gwat-beacon более отказоустойчивой.
Достигается за счет разбиения процесса синхранизации на этапы:
1. __Чекпоинт-синхронизация__ Автономная синхронизация gwat и beacon до состояния последнего чекпоинта. Выполняются паралельно и не предусматриваент взаимодействия.  Gwat и Beacon ноды соединяется с удаленными пирами и запускют процесс синхронизации финализированной цепи и синхронизируется до последнего чекпоинта.
1. __Даг-синхронизация__ Загрузка блоков следующих после последнего чекпоинта. Тоже выполняется паралельно. 
1. __Хед-синхронизация__ предполагает два этапа
    1. Переход в режим готовности хед-синхронизации. Нода связки, которая первой завершила синхронизацию, ждет вторую. В это время принимает и с соответствующими ограничениями обрабатывает входящие блоки из своей сети. Бикон в этом режиме переодически шлет апи запросы в живат о готовности.
    1. Выполнение хед-синхронизации. Как только Бикон по апи получает сигнал готовности живат - запускает хед-синхронизацию. Бикон отправляет в живат данные по финализации вплоть до последнего рабочего сотояния.
1. Переход в рабочий режим. 


> __последний чекпоинт__ - __`current_justified`__
>
> В общем виде слот `current_justified` блока по отношению к текущему хеду при нормальной работе можно определить как
> (`<текущая эпоха> - 2`)*SlotsPerEpoch + 1
> 
> бикон при загрузке с существующей бд устанавливает этот чекпоинт как хед цепи.
>
> __Note:__ расчет эпохи по слоту `floor(slot/SlotsPerEpoch)` 



## GWAT

### Чекпоинт-синхронизация
Особых изменений не требуется, текущей реализации __синхронизации финализированной цепи__ достаточно.
В качестве опциональной доработки можно сделать:
1. Прекращение финализации блоков при достижении последнего чекпоинта

### Даг-синхронизация
Особых изменений не требуется, текущей реализации __синхронизации даг цепи__ вполне хватает.
По доработкам сделать:
1. ~~Добавить дополнительное состояние даг-синхронизации.~~
    > в настоящий момент состояние синхронизации определяется только для __синхронизации финализированной цепи__ (метод Synchronising, статистика, апи), при синхронизации дага  IsSync==false, нода параллельно работает в штатном режиме (обрабатывает пропагейт etc.). Такое положение дел вызывает ряд сложностей, в частности потенциальнию возможность криейта блоков, запроса кандидатов и финализации, что в связке с биконом может привести к нарушению работы сети.
    
    1. ~~По аналогии методом `func (d *Downloader) Synchronising() bool` добавить метод `DagSynchronising`, который возвращает true на время даг-синка~~
    2. ~~Посмотреть апи по синхронизации и реализовать аналогичное для даг-синка или дополнить существующее~~
    3. ~~Блокировать работу даг-апи (sync, getCandidates, finalize) при даг-синхронизации. Возвращайть ошибку как и при обычной синхронизации (проверить)~~
    4. ~~Проверить/реализовать корректное создание BlockDag для загружаемых блоков и заполнение типсов (по аналогии с пропагейтом блоков)~~

1. ~~Проверить параллельную работу пропагейта блоков при даг-синке~~
1. ~~Валидация загружаемых блоков идентично этому [Block propagation](https://hackmd.io/hk6NZnAsS-yRudunJpqKtA?both#Block-propagation)~~

### Хед-синхронизация

#### Режим готовности хед-синхронизации
1. ~~Добавить дополнительное состояние хед-синхронизации.~~
    1. По аналогии методом `func (d *Downloader) Synchronising() bool` добавить метод `HeadSynchronising`, который возвращает true на время хед-синка
    2. Посмотреть апи по синхронизации и реализовать аналогичное для хед-синка или дополнить существующее
    3. Блокировать работу даг-апи (sync, getCandidates, finalize) при хед-синхронизации. Возвращайть ошибку как и при обычной синхронизации (проверить)
1. ~~Добавить даг-апи для сигнализации о готовности синхронизации от бикона Бикона `dag_headSyncReady` (активно при хед-синке)~~
    1. __params:__ { checkpoint: ConsensusInfo } // Slot, Creators, Finalizing 
    1. __response:__ success bool or error 
    1. __handling:--  
        1. получить блок последнего кандидата
        1. провалидировать как спайн:
            - проверить финализацию 
            - равенство номера и высоты
            - стейт (получить по руту) 
        1. если есть финализированные блоки после чекпоинта - перестроить даг-цепь (либо провалидировать на этапе выполнеия хед-синка, что проще или оптимальнее)
            - собрать все финализированные блоки с номером больше чекпоинта
            - очистить данные финализации 
            - создать BlockDag
            - заполнить заново типсы 

1. [Опционально] если долго нет запросов от бикона на апи хед-синхронизации, либо финализации, что приводит к росу дага, после заданного кол-ва слотов запускать процесс синхронизации с удаленными нодами.

#### ~~Выполнение хед-синхронизации~~
4. ~~Добавить даг-апи для получения данных от бикона Бикона~~ `dag_headSync`
    1. __params:__ { data: []ConsensusInfo } //наверное разбивать на чанки смысла нет
    1. __response:__ success bool or error 
    1. __handling:-- для каждого ConsensusInfo
        - сохранить слот-криейтор
        - собрать цепочки финализации для каждого спайна
        - провалидировать
        - если блок не финализирован выполнить финализацию (на эту тему см. `dag_headSyncReady` выше)
    1. Разработать поведение при ошибке выполнения
        - прерываем синхронизацию, возвращаем ошибку

### ~~Переход в рабочий режим~~
1. Сбросить все статусы синхронизаций. Должны заблокироватся все апи синхронизации и разблокироватся апи штатной работы


### func (d *Downloader) syncWithPeerUnknownDagBlocks
исправить

--------------------------------



## BEACON

> Note: в биконе есть `package sync` в `beacon-chain/sync/`:
>   >Package sync includes all chain-synchronization logic for the beacon node, including gossip-sub validators for blocks, attestations, and other p2p messages, as well as ability to process and respond to block requests by peers.
>
> нас интересует подпакет `package initialsync` в `beacon-chain/sync/initial-sync`:
> > Package initialsync includes all initial block download and processing logic for the beacon node, using a round robin strategy and a finite-state-machine to handle edge-cases in a beacon node's sync status.
>



### Чекпоинт-синхронизация
Изменений не требуется в текущей реализации.

### Даг-синхронизация
Предварительно особых изменений не требуется. 
1. Требуется ресерч по процедурам на момент завершения синхронизации для запуска хед-синка (там кажется есть таймер который проверяет синхронизацию/готовность gwat, там наверное нужна доработка)

`beacon-chain/sync/initial-sync/service.go:73`
````go
// Start the initial sync service.
func (s *Service) Start() {
    ...
	if err := s.roundRobinSync(genesis); err != nil {
        // main sync process ^^^^^^^^^^^^^^^^^^^^^^^
	}
	log.Infof("Synced up to slot %d", s.cfg.Chain.HeadSlot())
	s.markSynced(genesis)
}
````
`beacon-chain/sync/initial-sync/round_robin.go:40`
````go
// Round Robin sync looks at the latest peer statuses and syncs up to the highest known epoch.
//
// Step 1 - Sync to finalized epoch.
// Sync with peers having the majority on best finalized epoch greater than node's head state.
//
// Step 2 - Sync to head from finalized epoch.
// Using enough peers (at least, MinimumSyncPeers*2, for example) obtain best non-finalized epoch,
// known to majority of the peers, and keep fetching blocks, up until that epoch is reached.
func (s *Service) roundRobinSync(genesis time.Time) error {
    ...
	// Step 1 - Sync to end of finalized epoch.
	if err := s.syncToFinalizedEpoch(ctx, genesis); err != nil {
		return err
	}

	// Already at head, no need for 2nd phase.
	if s.cfg.Chain.HeadSlot() == slots.Since(genesis) {
		return nil
	}

	// Step 2 - sync to head from majority of peers (from no less than MinimumSyncPeers*2 peers)
	// having the same world view on non-finalized epoch.
	return s.syncToNonFinalizedEpoch(ctx, genesis)
}

// syncToFinalizedEpoch sync from head to best known finalized epoch.
func (s *Service) syncToFinalizedEpoch(ctx context.Context, genesis time.Time) error {
    ...
	for data := range queue.fetchedData {
		s.processFetchedData(ctx, genesis, s.cfg.Chain.HeadSlot(), data)
	}
//     Здесь выполняется подкключение к GWAT
/*
    ...
    time="2022-09-23 20:14:29" level=info msg="Processing block batch of size 64 starting from  0x6fb24d00... 448/583 - estimated time remaining 4s" blocksPerSecond=28.8 peers=1 prefix=initial-sync
    time="2022-09-23 20:14:30" level=info msg="Processing block batch of size 64 starting from  0x8d31a3d8... 512/584 - estimated time remaining 2s" blocksPerSecond=31.9 peers=1 prefix=initial-sync
    time="2022-09-23 20:14:37" level=info msg="Connected to new endpoint: /***" prefix=powchain
    time="2022-09-23 20:14:38" level=info msg="Synced to finalized epoch - now syncing blocks up to current head" currentSlot=586 prefix=initial-sync syncedSlot=575
    time="2022-09-23 20:14:39" level=info msg="Processing block 0x0ca5017f... 576/586 - estimated time remaining 0s" blocksPerSecond=32.0 peers=1 prefix=initial-sync
*/
	log.WithFields(logrus.Fields{
		"syncedSlot":  s.cfg.Chain.HeadSlot(),
		"currentSlot": slots.Since(genesis),
	}).Info("Synced to finalized epoch - now syncing blocks up to current head")
    ...
	return nil
}


// syncToNonFinalizedEpoch sync from head to best known non-finalized epoch supported by majority
// of peers (no less than MinimumSyncPeers*2 peers).
func (s *Service) syncToNonFinalizedEpoch(ctx context.Context, genesis time.Time) error {
	for data := range queue.fetchedData {
		s.processFetchedDataRegSync(ctx, genesis, s.cfg.Chain.HeadSlot(), data)
	}
    
    // Тут уже работает стандартная обработка логов "onBlock" (beacon-chain/blockchain/process_block.go:91)
    // "Calculation of finalization sequence" - лог из Спайнов      
    /*
    ...
    time="2022-09-23 20:14:39" level=info msg="Calculation of finalization sequence" lastFinHash=0x028065daab563d75cedcd0abca879cf4afa4aa8072182e37cb49f79708daf7cd lastFinSlot=574 prefix=blockchain spines=[0xfd78e3d36a0b635072ef98a88a5ca46cad23764e4ffb181c90c166edf7507bd0 0x2c488a216172b279deb518893b7d5f195f7400e3f5a78b0e6fe9f06a2a58c83d]
    time="2022-09-23 20:14:39" level=info msg="Synced new block" block=0x3bc8c603... epoch=18 finalizedEpoch=16 finalizedRoot=0x8d31a3d8... parentRoot=0x072a7efb... prefix=blockchain slot=586 slotInEpoch=10 version=altair
    time="2022-09-23 20:14:39" level=info msg="Finished applying state transition" attestations=1 prefix=blockchain slot=586 syncBitsCount=512

    time="2022-09-23 20:14:39" level=info msg="Synced to head of chain" currentSlot=586 prefix=initial-sync syncedSlot=586
    time="2022-09-23 20:14:39" level=info msg="Synced up to slot 586" prefix=initial-sync
    */
	log.WithFields(logrus.Fields{
		"syncedSlot":  s.cfg.Chain.HeadSlot(),
		"currentSlot": slots.Since(genesis),
	}).Info("Synced to head of chain")
	if err := queue.stop(); err != nil {
		log.WithError(err).Debug("Error stopping queue")
	}

	return nil
}
````




### Хед-синхронизация

1. ~~Добавить методы `ExecutionDagHeadSyncReady` и`ExecutionDagHeadSync`   в `beacon-chain/powchain/dag_client.go`, которые вызывают gwat апи `dag_headSyncReady` и `dag_headSync` соответственно.~~ 

#### Режим готовности хед-синхронизации. запуск процедуры после завершения даг-синка:
1. Запуск цикла по таймеру (наверное 1 сек) 
1. Подготовить параметры запроса `dag_headSyncReady`
   - проверить соединение с gwat (`beacon-chain/powchain/service.go:335` `func (s *Service) IsConnectedToETH1() bool`), если нет - continue 
   - получить стейт/блок `current_justified` чекпоинта
   - получить
       - slot
       - Eth1Data.finalizing (спайны финализации)
       - Creators (пока не очень понятно как это реализовать, возможно можно вычислить. см. `func BeaconCommitteeFromState(ctx context.Context, state state.ReadOnlyBeaconState, slot types.Slot, committeeIndex types.CommitteeIndex) ([]types.ValidatorIndex, error)`, но вызов затратный, и надо проверить работает ли в прошлом. Текущая логика назначения криейторов тут `beacon-chain/blockchain/creators.go:27` `func (s *Service) GetCurrentCreators() ([]gwatCommon.Address, error)`. Еще как вариант сохранять в блок.)
   - отправить запрос, получить ответ:
       - fail - continue 
       - success - завершить процесс и переход к следующему этапу

#### Выполнение хед-синхронизации
1. Подготовить параметры запроса `dag_headSync`
   - получить все стейты(как вариант блоки, но сейчас работаем по стейтам) от текущего слота до `current_justified` чекпоинта
   - для каждого стейта получить
       - slot
       - Eth1Data.finalizing 
       - Creators
   - отправить запрос, получить ответ:
       - fail - надо понять что может быть не так, разработать обработку 
       - success - завершить процесс и переход в рабочий режим

### Переход в рабочий режим
1. Проверить переход к штатному режиму работы

### Misc

#### основная проверка синхронизированности бикона
`beacon-chain/sync/rpc_status.go:110`
````go
// shouldReSync returns true if the node is not syncing and falls behind two epochs.
func (s *Service) shouldReSync() bool {
	syncedEpoch := slots.ToEpoch(s.cfg.chain.HeadSlot())
	currentEpoch := slots.ToEpoch(s.cfg.chain.CurrentSlot())
	prevEpoch := types.Epoch(0)
	if currentEpoch > 1 {
		prevEpoch = currentEpoch - 1
	}
	return s.cfg.initialSync != nil && !s.cfg.initialSync.Syncing() && syncedEpoch < prevEpoch
}
````