* fix: sync: fail sync instead of logging if we sync the wrong chain
* fix: sync: write headers in the correct order
Just in case. This shouldn't be necessary, but we might as well.
* fix: minus minus
* fix: do put the tipset
Put != Persist
And fix the message to account for the fact that we now reject _old_
blocks along with new ones.
We frequently receive "out of date" blocks in hello messages from
syncing and/or out of sync nodes. This isn't an error.
This will reject blocks in pubsub validation if they're either:
1. Too far into the future (5 blocks beyond the expected head).
2. Too far into the past (before finality with respect to our current
head).
Specifically:
1. We were previously rejecting future blocks in the sync logic, but not
in pubsub itself.
2. We never used to check if a block was too _old_.
Motivation: Blocks that are too new/too old can cause us to perform
quite a bit of unnecessary work.
This paves the way for better object lifetime management.
Concretely, it makes it possible to:
- have different stores backing chain and state data.
- having the same datastore library, but using different parameters.
- attach different caching layers/policies to each class of data, e.g.
sizing caches differently.
- specifying different retention policies for chain and state data.
This separation is important because:
- access patterns/frequency of chain and state data are different.
- state is derivable from chain, so one could never expunge the chain
store, and only retain state objects reachable from the last finality
in the state store.
This is implicitly checked when verifying the miner signature, but
explicitly checking it here makes it clear that this is a protocol
requirement, and reduces the chances that it will be accidentally
"refactored out".
If block 1 was a null block then blockSet would include genesis which
would lead to us trying to load parent of a genesis block.
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
1. SyncerState contains a mutex and should never be copied. Honestly, this case
was probably fine but it's just as easy to create a separate snapshot type and
easier to reason about.
2. We need to initialize the syncStates array once at start, before accessing
it. By each syncer state inside each worker, we were racing with calls to
`State()`. Again, this was probably benign, but I don't trust optimizing
compilers.