The (somewhat) redundant sync_head MMR impl was removed and cleanup up in https://github.com/mimblewimble/grin/pull/3556.
This never worked as intended as it did not allow header_head and sync_head to diverge successfully beyond a single batch (512) of headers. We also did not require sync_head to be persisted in the db across node restarts (sync_head would be reset during header sync).
This PR moves the tracking of sync_head into the HeaderSync sync status that we already track.
This allows a sync_head to diverge from current header_head in a scenario where multiple batches of headers are required to successfully sync the fork.
We now track a sync_head on a header fork even if the cumulative difficulty on a given batch of headers is (temporarily) lower than the current header_head.
The issue that this fixes is as follows -
local node sees a new header fork based on a peer advertising greater cumulative difficulty (total work).
we rewind from header_head (diff X) and process a batch of 512 headers on the fork
this results in diff Y on the fork
Y < X so header_head is not updated
we now fail to sync additional headers on the fork as "locator" is based on header_head
we keep syncing the initial 512 headers on the fork and do not make further progress along the fork
The changes in this PR ensure we now correctly track sync_head which in the scenario above would be the last_header of the initial batch of headers on the new fork.
This allows the "locator" to be constructed correctly and progress to be made along the fork.
Resolves #3615
We could have implemented this storing the sync_head in the db but this PR attempts to take advantage of the existing sync_status tracking. We only really need to track sync_head when we are in HeaderSync state for an extended period of time.
We can likely extend this in two ways in future work -
rework the sync state to allow us to track the sync_peer and the various data for stalling etc. during HeaderSync so we can consolidate all the data being tracked during the sync process (currently spread all over header_sync.rs)
track head and associated data in a similar way during BodySync (mainly for consistency)
The work for (1) is a little convoluted as sync state is defined in chain crate currently and has no knowledge of peers etc. We need to spend a bit of time thinking through the best way of approaching this without making the code really awkward.
But it would open up a relatively clean way of tracking a single peer during header_sync. i.e. We keep asking the same peer for subsequent headers until we either sync with that peer successfully or the peer disconnects.
This PR also adds an additional conditional check during check_run in header_sync -
// Quick check - nothing to sync if we are caught up with the peer.
if peer_diff <= sync_head.total_difficulty {
return Ok(false);
}
This prevents our local node from flapping between BodySync and HeaderSync toward the end of IBD.
The (somewhat) redundant sync_head MMR impl was removed and cleanup up in https://github.com/mimblewimble/grin/pull/3556. This never worked as intended as it did not allow
header_head
andsync_head
to diverge successfully beyond a single batch (512) of headers. We also did not requiresync_head
to be persisted in the db across node restarts (sync_head
would be reset during header sync).This PR moves the tracking of
sync_head
into theHeaderSync
sync status that we already track. This allows async_head
to diverge from currentheader_head
in a scenario where multiple batches of headers are required to successfully sync the fork.We now track a
sync_head
on a header fork even if the cumulative difficulty on a given batch of headers is (temporarily) lower than the currentheader_head
.The issue that this fixes is as follows -
header_head
(diffX
) and process a batch of 512 headers on the forkY
on the forkY < X
soheader_head
is not updatedheader_head
The changes in this PR ensure we now correctly track
sync_head
which in the scenario above would be thelast_header
of the initial batch of headers on the new fork. This allows the "locator" to be constructed correctly and progress to be made along the fork.Resolves #3615
We could have implemented this storing the
sync_head
in the db but this PR attempts to take advantage of the existing sync_status tracking. We only really need to tracksync_head
when we are inHeaderSync
state for an extended period of time.We can likely extend this in two ways in future work -
sync_peer
and the various data forstalling
etc. duringHeaderSync
so we can consolidate all the data being tracked during the sync process (currently spread all over header_sync.rs)head
and associated data in a similar way duringBodySync
(mainly for consistency)The work for (1) is a little convoluted as sync state is defined in
chain
crate currently and has no knowledge of peers etc. We need to spend a bit of time thinking through the best way of approaching this without making the code really awkward.But it would open up a relatively clean way of tracking a single peer during
header_sync
. i.e. We keep asking the same peer for subsequent headers until we either sync with that peer successfully or the peer disconnects.This PR also adds an additional conditional check during
check_run
inheader_sync
-This prevents our local node from flapping between
BodySync
andHeaderSync
toward the end of IBD.