# Geth codebase learning ## Engine API ### newPayload > Insert the given payload into the local chain. It's how the local chain be extended. 1. Lock (defer UnLock) 2. ExecutableDataToBlock - Construct a block from executable data 3. api.lastNewPayloadUpdate = time.Now() - Record receiving time. Will warning the user if hearbeat mechanism detects not receiving new payload from beacon client for long time. 4. Return success if the block is already in local storage 5. Deplay importing payload if parent block is missing or snapSync is enabled - Question: When does snapSync mode be automatically enabled or disabled? - Question: Why payload import should be delayed if snapSync is enabled? 6. If parent state is not available, cache remote blocks and return accepted 7. Insert block without set head 1. Skip blocks that don't need to build snapshot 2. Verify header 3. Get parent block and state to execute on top 4. Validate block and write it and its state to database(might not set head) 8. return success ### getPayload > Retrieve the pre-generated payload associated with th ID. This payload will be used for consensus sealing at the beacon client level. 1. Get block in cache of local payloads generated - local block is generated by the execution client itself, which triggerd by `forkChoiceUpdated` - remote block is received from other clients, which triggered by `newPayload` ### forkChoiceUpdated > 1. Update the canonical chain marker > 2. Generate the payload(pending blocks) with the given attributes(it's how the pending transactions be packed as a new block proposal) 1. The first parameter of forkChoiceUpdate contains three block hashes: HeadBlockHash(latest), SafeBlockHash(justified) and FinalizedBlockHash(finalized). Each of these blocks needs to be verified if it exists in database and if it's in canonical chain. 2. Verify HeadBlock - If HeadBlock is not in database. Start **BeaconSync** if it's valid, or return invalid. - If HeadBlock is not canonical, **SetCanonical**. - If HeadBlock is older than current block, ignore it. 3. Verify SafeBlock - If SafeBlock is not in database or is not in canonical chain, return invalid. - Else, mark SafeBlock. 4. Verify FinalizedBlock - If FinalizedBlock is not in database or is not in canonical chain, return invalid. - Else, mark FinalizedBlock. 5. The second parameter is payloadAttributes. - If payloadAttributes is not nil, start **BuildPayload**. ### SetCanonical > Set the new head block as the given block 1. Find the closest ancestor of the given block with available state and re-execute(recover) all the ancestor blocks since that.Here the re-execution logic is the same as inserting block logic used in `newPayload`. 2. If the given block's parent is not current block, `Reorg` happens. `Reorg` finds the closest common ancestor of old chain and new chain, then: - Extend the old chain if old chain's head block is ancestor of the given block. - Reconstruct the chain if old chain's head block is not ancestor of the given block. - File an issue if the given block is in old chain. 3. Set the given block as head block of canonical chain. ### BeaconSync > BeaconSync contains two main process: 1) backfilling header chain 2) backfilling state and chain data. Unlike chain synchronization in PoW which is in a forward fashion, BeaconSync introduces skeleton, which represents backfilling process for header chain. 1. Init sync ![upload_dc1f506d7cf0ad7bbb491596838e3efa](https://hackmd.io/_uploads/S1BsUf9wC.png) 2. If subchains are linked, start backfilling state and chain data. Or, asking idle peers for missed headers till subchains are linked. 3. Set the common ancestor of the skeleton header chain and the local chain to Block_Origin; Set the pivot block of skeleton header chain to Block_Pivot(pivot number can be seen as the number of the most recent finalized block). In snap sync, Block_Origin should be set to (Block_Pivot-1) if it's newer than Block_Pivot. 4. Fetch headers/bodies/receipts from peers and complete the local chain. ### BuildPayload 1. Generate a payload with no transactions included to return to ensure no missing slot. 2. Start a goroutine to update the payload in background. 3. TODO: `Filter transactions from pools` ## Questions 1. When does snapSync mode be automatically enabled or disabled? - snapSync mode is disabled while: - eth/downloader/beaconsync.go -> resume() -> b.success() If backfilling synchronization succeeds, notify the outer context to disable snap syncing and enable transaction propagation. - eth/catalyst/api.go -> forkchoiceupdated -> api.eth.SetSynced() Disable snapSync mode if the head block in FCU call is already in local storage. - eth/handler.go -> newHandler() snapSync mode is requested but head block and state are already in local storage. - snapSync is enabled while: - eth/handler.go -> newHandler() - snapSync mode is requested and database is emtpy - if the user manually (or via a bad block) rolled back a snap sync node below the sync point or the last snap sync is not finished while user specifies a full sync this time 2. Why payload import should be delayed if snapSync is enabled? When a node is in snapSync mode, it runs state-sync to retrieve state data from peers and update local database rather than imports the payload from consensus client. - check PR https://github.com/ethereum/go-ethereum/pull/25210 - check state trie related FAQ https://geth.ethereum.org/docs/faq