Block validation in Chia is composed of two parts: header validation and body validation.
The header validation performs consensus algorithm-related checks, such as proof of space and time, signage points and infusion points, previous block hashes, foliage hashes, and timestamps. Notably, it does not validate any CLVM, coin spends, or signatures. Usually, for efficiency, light clients will want to validate headers but not the body.
Body validation entails running all puzzles for spent coins, reading the coin database, verifying signatures, checking for duplicate or invalid removals and additions, etc.
Validating a block in Chia will require access to some blocks in the past, up to a maximum theoretical value of three times the max number of blocks in a slot (3x128=384), but usually only a few are needed. Also, information regarding previous sub-epochs and epochs is needed for validation, as well as the current system timestamp. Implementations
can cache only some recent blocks instead of storing all blocks in memory.
chia-blockchain maintains a database of BlockRecords, which contain only the important pieces of block information required for validating future blocks.
Full Sync vs Normal Operation
There are two cases when a node might verify blocks.
- During a full sync, where the node is trying to catch up to the most recent block, starting from an old block height. In this case, the node is able to download many blocks at once.
- During normal operation, where the node is caught up to the most recent block, and is only downloading one block every few seconds.
We'll cover both of these cases below.
Full sync is the process by which a full node downloads and validates all of the blocks in the blockchain and catches up to the most recent block. Full sync is important, because it allows new nodes to validate that a blockchain is the heaviest -- and thus, the currently valid -- chain. It allows everyone to come to consensus on the current state, regardless of when they come online, or for how long they go offline.
The method of full sync can vary between implementations, but the high level algorithm is the following:
- Connect to other peers on the network, by querying the DNS introducer, and crawling the network.
- Check the current weight of the peak of the peers, and select a few peers to sync from.
- Download and validate a weight proof, to ensure that the given peak has real work behind it.
- Download and validate all blocks in the blockchain, in batches.
Weight proofs are important, because they prevent other peers from lying to us about what the heaviest peak is. They also prevent us from downloading potentially useless data. Once the full node is caught up to the blockchain, it can properly farm, access the coin state, etc.
Normal operation is the process by which a full node continuously gossips and receives blocks with other peers, always following the heaviest peak. If our node is at weight 2000, and we see that a peer has a peak at weight 2100, then we fetch that block from the peer. Usually, this is done in two phases:
- The unfinished block is propagated across the network, along with all information up to the signage point, transactions, etc.
- The finished block, which includes infusion point VDFs, is also propagated. This typically excludes the transactions, which were already sent in step 1.
Normal operation is much less CPU-intensive than full sync, since there is only one block every 18 seconds, and one transaction block every 52 seconds, on average. Low-power machines like the Raspberry PI 4 should be able to easily continue normal operation.
Block Validation Steps
The following sections list all of the required checks to ensure validity of a block. Please note that the official protocol and specification are defined by the
reference implementation, and not by this documentation page.
- Check that the previous block exists in the blockchain, or that it is genesis.
- Check finished slots that have been crossed since
prev_b= the previous block in the chain.
- Check sub-slot challenge hash for genesis block.
- Check sub-slot challenge hash for non-genesis block.
- Check sub-slot challenge hash for empty slot.
- Validate that genesis block has no ICC=Infused challenge chain.
- Validate that there is not icc iff icc_challenge hash is None.
- Check infused challenge chain sub-slot VDF.
- Check infused challenge sub-slot hash in challenge chain, deficit 16.
- Check infused challenge sub-slot hash not included for other deficits.
- Check infused challenge sub-slot hash in reward sub-slot.
- If no icc, check that the cc=challenge chain doesn't include it.
- If no icc, check that the rc=reward chain doesn't include it.
- Check sub-epoch summary hash is None for empty slots.
- Check new difficulty and ssi if applicable.
- Check new difficulty and ssi are None if we don't finish epoch.
- Check challenge sub-slot hash in reward sub-slot.
- Check end of reward slot VDF.
- Check challenge chain sub-slot VDF.
- Check deficit (MIN_SUB.. deficit edge case for genesis block)
- If prev sb had deficit 0, resets deficit to MIN_BLOCK_PER_CHALLENGE_BLOCK
- Otherwise, deficit stays the same at the slot ends, cannot reset until 0
- Check sub-epoch summary
- Check that genesis block does not have sub-epoch summary
- Check that we finished a slot and we finished a sub-epoch
- Check the actual sub-epoch is correct
- Check that we don't have to include a sub-epoch summary
- Check if the number of blocks is less than the max
- Check proof of space
- Check signage point index
- Check required iters
- check signage point index 0 has no cc sp and no rc sp
- Check no overflows in the first sub-slot of a new epoch
- Check total iters
- Check reward chain sp proof
- Check reward chain sp signature
- Check cc sp vdf
- Check cc sp sig
- Check is_transaction_block
- Check foliage block signature by plot key
- Check foliage block signature by plot key
- Check unfinished reward chain block hash
- Check pool target max height
- Check pre-farm puzzle hashes for genesis block.
- If pospace has a pool pk, check pool target signature. Should not check this for genesis block.
- Otherwise, the plot is associated with a contract puzzle hash, not a public key, so check pool contract ph
- Check extension data if applicable. None for mainnet.
- Check if foliage block is present
- Check foliage block hash
- Check prev block hash for genesis and non-genesis
- The filter hash in the Foliage Block must be the hash of the filter
- The timestamp in Foliage Block must not be over 5 minutes in the future, and the timestamp must be greater than the previous transaction block timestamp
- Check block height for genesis and non-genesis
- Check block weight for genesis and non-genesis
- Check challenge chain infusion point VDF
- Check reward chain infusion point VDF
- Check infused challenge chain infusion point VDF
- Check reward block hash
- Check reward block is_transaction_block
- For non transaction-blocs: foliage block, transaction filter, transactions info, and generator must be empty. If it is a block but not a transaction block, there is no body to validate. Check that all fields are None, and return.
- For blocks, foliage block, transactions info must not be empty.
- The transaction info hash in the Foliage block must match the transaction info.
- The foliage block hash in the foliage block must match the foliage block.
- The reward claims must be valid for the previous blocks, and current block fees.
- No transactions before INITIAL_TRANSACTION_FREEZE timestamp (this check has been removed).
- The generator root must be the hash of the serialized bytes of the generator for this block (or zeroes if no generator)
- Check the transactions generator reference list:
- The generator_ref_list must be the hash of the serialized bytes of
- the generator ref list for this block (or 'one' bytes [0x01] if no generator)
- The generator ref list length must be less than or equal to MAX_GENERATOR_REF_LIST_SIZE entries
- The generator ref list must not point to a height >= this block's height
- If we have a generator reference list, we must have a generator
- Check that cost <= MAX_BLOCK_COST_CLVM
- The CLVM program must not return any errors
- Check that the correct cost is in the transactions info
- Check additions for max coin amount (be careful to check for 64 bit overflows in other languages. This is the max 64 bit unsigned integer)
- Validate addition and removal merkle set roots.
- The additions and removals must result in the correct filter.
- Check for duplicate outputs in additions.
- Check for duplicate spends inside block.
- Check if removals exist and were not previously spent. (coin_db up to the fork point + fork block + this_block). Be careful with forks and with ephemeral coins (added and removed in same block).
- Check that the total coin amount for added is <= removed.
- Check that the assert fee sum <= fees, and that each reserved fee is non-negative.
- Check that the fee amount + farmer reward < maximum coin amount.
- Check that the computed fees are equal to the fees in the block header.
- Verify that removed coin puzzle_hashes match with calculated puzzle_hashes.
- Verify CLVM conditions.
- Verify aggregated signature.