I would like to know if there's a way to download a blockchain without knowing block headers. This could be achieved by asking NEXT_BLOCK_HASH from the genesis block. But is this even possible ?
Yes, this is supported by the P2P protocol. In fact, in the very first release of the Bitcoin software there was no way to ask for headers separately.
The process works roughly like this:
- A sends
getblocksto B (with a "locator", that roughly informs B about what the last block is that A already has).
- B responds with
invmessage containing (up to) 500 block hashes (just the hashes, not the full headers), starting from the last block A and B have in common.
- A then decides which of those block hashes it does not yet know, and sends a
getdatarequest to fetch those.
- B then responds by sending
blockmessages for each of the requested blocks.
- When A reaches the end of the 500 hashes in the
inv, it issues another
getblocks, now with an updated locator, so the process can restart with further blocks.
The big downside of this approach is that the progress is "in the dark" - A does not know if the blocks it is receiving from B will amount to anything interesting (e.g. they could be from an old fork, or even a low-work attack chain), which A will only notice when it has downloaded and processed them all. The headers-first synchronization gives A the ability to decide whether the chain is interesting before trying to actually download it, with only a marginal bandwidth increase (block hashes are 32 bytes, block headers are 81 bytes each). With headers-first, the process is instead:
- A sends a
getheadersto B, with a locator corresponds to the best header it has so far.
- B responds with a
headersmessage, containing up to 2000 headers starting at the last common header A and B have.
- A processes those headers, and sends another
getheadersfrom the newly updated best header it has, repeating the process, gathering all the headers.
- At some point, A decides that the headers it has received from B are a sufficiently interesting chain (= its accumulated proof of work is high enough). When this happens, it starts sending
getdatamessages to B for the blocks along the header chain it lacks.
- B responds to those using
blockmessages, which A can process normally.