erigon-pulse/eth/stagedsync
2023-03-30 18:03:29 -05:00
..
stages Remove erigon_issuance & erigon_watchTheBurn (#6685) 2023-01-24 22:54:08 +01:00
all_stages.go add more sync stages to metrics (#5740) 2022-10-18 10:22:57 +07:00
chain_reader.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
default_stages.go Add Prague fork support (#7005) 2023-03-02 17:51:36 +00:00
exec3.go e3: don't loose last txnum (#7028) 2023-03-06 09:19:30 +00:00
README.md Link ETL Repository (#6573) 2023-01-13 17:45:32 +07:00
stage_blockhashes.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_bodies_test.go WIP: cmd, turbo, core, eth: TransactionsV3 flag and persist in new table (#6754) 2023-02-24 18:49:25 +00:00
stage_bodies.go Headers POW updated (#6983) 2023-03-03 16:09:46 +00:00
stage_call_traces_test.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_call_traces.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_cumulative_index.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_execute_test.go e3: less locks, less casts (#6991) 2023-03-01 07:59:56 +00:00
stage_execute.go erigon3 snapshots version 3 (#6811) 2023-02-09 13:51:51 +07:00
stage_finish.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_hashstate_test.go HashState: parallel promote cleanly (#6512) 2023-01-09 11:01:21 +07:00
stage_hashstate.go e3: getLogs on iterators (#6683) 2023-01-25 16:29:41 +07:00
stage_headers.go Refactor and simplify PulseChain integration 2023-03-30 18:03:29 -05:00
stage_indexes_test.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_indexes.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_interhashes_test.go WIP: cmd, turbo, core, eth: TransactionsV3 flag and persist in new table (#6754) 2023-02-24 18:49:25 +00:00
stage_interhashes.go e3: getLogs on iterators (#6683) 2023-01-25 16:29:41 +07:00
stage_log_index_test.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_log_index.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_mining_create_block.go blockValue should use gasUsed rather than gasLimit (#6875) 2023-02-14 15:05:27 +00:00
stage_mining_exec.go WIP: cmd, turbo, core, eth: TransactionsV3 flag and persist in new table (#6754) 2023-02-24 18:49:25 +00:00
stage_mining_finish.go blockValue should use gasUsed rather than gasLimit (#6875) 2023-02-14 15:05:27 +00:00
stage_postexec.go Bor fixes (#3553) 2022-02-24 00:03:10 +00:00
stage_senders_test.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_senders.go Senders: nil check (#6717) 2023-01-27 10:19:33 +07:00
stage_snapshots.go Initial PulseChain integration 2023-03-30 18:03:22 -05:00
stage_txlookup.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage_verkle_trie.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
stage.go Avoid unnecessary unwind+rewind (#6712) 2023-01-30 15:42:23 +01:00
stagebuilder.go Fixing hive SideChain reorg test (#5620) 2022-10-05 05:42:38 +01:00
sync_test.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
sync.go Break out of staged sync step when bad block is found (#7025) 2023-03-06 07:58:44 +00:00
testutil.go reference hash, address, and chain config from lib (#6536) 2023-01-13 18:12:18 +00:00
types.go rename (#1978) 2021-05-20 19:25:53 +01:00

Staged Sync

Staged Sync is a version of Go-Ethereum's Full Sync that was rearchitected for better performance.

It is I/O intensive and even though we have a goal on being able to sync the node on an HDD, we still recommend using fast SSDs.

Staged Sync, as its name suggests, consists of 10 stages that are executed in order, one after another.

How The Sync Works

For each peer Erigon learns what the HEAD blocks is and it executes each stage in order for the missing blocks between the local HEAD block and the peer's head blocks.

The first stage (downloading headers) sets the local HEAD block.

Each stage is executed in order and a stage N does not stop until the local head is reached for it.

That mean, that in the ideal scenario (no network interruptions, the app isn't restarted, etc), for the full initial sync, each stage will be executed exactly once.

After the last stage is finished, the process starts from the beginning, by looking for the new headers to download.

If the app is restarted in between stages, it restarts from the first stage.

If the app is restarted in the middle of the stage execution, it restarts from that stage, giving it the opportunity to complete.

How long do the stages take?

Here is a pie chart showing the proportional time spent on each stage (it was taken from the full sync). It is by all means just an estimation, but it gives an idea.

Reorgs / Unwinds

Sometimes the chain makes a reorg and we need to "undo" some parts of our sync.

This happens backward from the last stage to the first one with one caveat that tx pool is updated after we already unwound the execution so we know the new nonces.

That is the example of stages order to be unwound (unwind happens from right to left).

state.unwindOrder = []*Stage{
		// Unwinding of tx pool (reinjecting transactions into the pool needs to happen after unwinding execution)
		stages[0], stages[1], stages[2], stages[9], stages[3], stages[4], stages[5], stages[6], stages[7], stages[8],
	}

Preprocessing with ETL

Some stages use our ETL framework to sort data by keys before inserting it into the database.

That allows to reduce db write amplification significantly.

So, when we are generating indexes or hashed state, we do a multi-step process.

  1. We write the processed data into a couple of temp files in your data directory;
  2. We then use a heap to insert data from the temp files into the database, in the order that minimizes db write amplification.

This optimization sometimes leads to dramatic (orders of magnitude) write speed improvements.

What happens after the Merge?

In the Proof-of-Stake world staged sync becomes somewhat more complicated, as the following diagram shows.

Stages (for the up to date list see stages.go and stagebuilder.go):

Each stage consists of 2 functions ExecFunc that progesses the stage forward and UnwindFunc that unwinds the stage backwards.

Most of the stages can work offline though it isn't implemented in the current version.

We can add/remove stages, so exact stage numbers may change - but order and names stay the same.

Stage 1: Download Headers Stage

During this stage we download all the headers between the local HEAD and our peer's head.

This stage is CPU intensive and can benefit from a multicore processor due to verifying PoW of the headers.

Most of the unwinds are initiated on this stage due to the chain reorgs.

This stage promotes local HEAD pointer.

Stage 2: Block Hashes

Creates an index of blockHash -> blockNumber extracted from the headers for faster lookups and making the sync friendlier for HDDs.

Stage 4: Download Block Bodies Stage

At that stage, we download bodies for block headers that we already downloaded.

That is the most intensive stage for the network connection, the vast majority of data is downloaded here.

Stage 5: Recover Senders Stage

This stage recovers and stores senders for each transaction in each downloaded block.

This is also a CPU intensive stage and also benefits from multi-core CPUs.

This stage doesn't use any network connection.

Stage 6: Execute Blocks Stage

During this stage, we execute block-by-block everything that we downloaded before.

One important point there, that we don't check root hashes during this execution, we don't even build a merkle trie here.

This stage is single threaded.

This stage doesn't use internet connection.

This stage is disk intensive.

This stage can spawn unwinds if the block execution fails.

Stage 7: Transpile marked VM contracts to TEVM

[TODO]

Stage 8: Generate Hashed State Stage

Erigon during execution uses Plain state storage.

Plain State: Instead of the normal (we call it "Hashed State") where accounts and storage items are addressed as keccak256(address), in the plain state them are addressed by the address itself.

Though, to make sure that some APIs work and keep the compatibility with the other clients, we generate Hashed state as well.

If the hashed state is not empty, then we are looking at the History ChangeSets and update only the items that were changed.

This stage doesn't use a network connection.

Stage 9: Compute State Root Stage

This stage build the Merkle trie and checks the root hash for the current state.

It also builds Intermediate Hashes along the way and stores them into the database.

If there were no intermediate hashes stored before (that could happend during the first initial sync), it builds the full Merkle Trie and its root hash.

If there are intermediate hashes in the database, it uses the block history to figure out which ones are outdated and which ones are still up to date. Then it builds a partial Merkle trie using the up-to-date hashes and only rebuilding the outdated ones.

If the root hash doesn't match, it initiates an unwind one block backwards.

This stage doesn't use a network connection.

Stage 10: Generate call traces index

[TODO]

Stages 11, 12, 13, and 14: Generate Indexes

There are 4 indexes that are generated during sync.

They might be disabled because they aren't used for all the APIs.

These stages do not use a network connection.

Account History Index

This index stores the mapping from the account address to the list of blocks where this account was changed in some way.

Storage History Index

This index stores the mapping from the storage item address to the list of blocks where this storage item was changed in some way.

Log Index

This index sets up a link from the [TODO] to [TODO].

Tx Lookup Index

This index sets up a link from the transaction hash to the block number.

Stage 15: Transaction Pool Stage

During this stage we start the transaction pool or update its state. For instance, we remove the transactions from the blocks we have downloaded from the pool.

On unwinds, we add the transactions from the blocks we unwind, back to the pool.

This stage doesn't use a network connection.

Stage 16: Finish

This stage sets the current block number that is then used by RPC calls, such as eth_blockNumber.