Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Work with the new dcrd reorg ntfn sequence #828

Merged
merged 12 commits into from
Nov 21, 2018

Conversation

chappjc
Copy link
Member

@chappjc chappjc commented Nov 19, 2018

Resolving #823

  • Modify collectionQueue to verify that new blocks are in sequence
  • Bump required dcrd JSON RPC version to 5.0.0
  • Create rpcutils.CommonAncestor function to provide full reorg details to each reorg handler and avoid redundant RPCs.
  • Rewrite each ChainMonitor's reorg handling. Each of dcrsqlite, dcrpg, and stakedb use a switchToSideChain function. With blockdata, ReorgHandler simply stores the new chain tip's block data in the reorgDataSavers, which is presently just the explorer UI update.

There is no longer a need for BlockConnectedHandler in dcrpg and dcrsqlite, so it is removed. The goroutines are no longer started of course, and the synchronous collectionQueue now includes only sdbChainMonitor.BlockConnectedSync and wsChainMonitor.BlockConnectedSync (the synchronous collection and storage functions for the stakedb and blockdata packages.).

In short, the roles of each data saver's ReorgHandler are:

  1. blockdata: Collect and store in the special reorgDataSavers the data for just the new chain tip block. This is presently used to update the explorer UI.
  2. stakedb: Disconnect the old chain blocks from the StakeDatabase, then connect the new chain blocks.
  3. dcrsqlite: Collect and store data for the new chain blocks. The data for the old chain is not presently removed as it is not critical.
  4. dcrpg: This is the most complex one. All data related to old chain blocks are updated to reflect their new side chain status. The blocks in the new chain (from a side chain) are stored in the DB, while updating existing records, which may be from the disconnected blocks.

The entire process is much simpler overall. One source of complexity is the new reorg queue in the notification package, and the new and expanded txhelpers.ReorgData type that adds the entire lists of blocks in the old and new chains as well as the common ancestor. The reorg queue is processed by notification.ReorgSignaler, which uses the new rpcutils.CommonAncestor to get the common ancestor and the full list of block in the old and new chains.

rpcutils.CommonAncestor finds the common ancestor and full list of block in the old and new chain by iteratively interrogating dcrd via RPC for blocks' previous block hash until the same hash is encountered for both chains at the same height. This approach is used as opposed to two other ways:

  1. Figuring out the common ancestor in each package's ReorgHandler using whatever data resources they have. This is redundant and more error prone.
  2. Going back from the old chain tip until dcrd indicates the chain has gone from side (-1 confirmations) to main (non-negative confirmations). This is not robust as there may be data races if subsequent reorganization takes place during the handling of an earlier reorg.

OnBlockConnected now relays the previous block hash from the new
block header to the collection queue for verification.
After sync, the best block height and hash are set in the collection
queue via (*collectionQueue).SetPreviousBlock.
When (*collectionQueue).ProcessBlocks verifies a block is insequence,
and calls each synchronous block connected hander, it then sets the
new block as the previous for validation of the next block.
Add (*wiredDB).GetBestBlockHeightHash to retrieve the height and hash
of the base DB's best block. Used after sync to initialize the
collection queue's previous block record.
Required dcrd JSON RPC version is 5.0.0
@chappjc chappjc added this to the 3.1 milestone Nov 19, 2018
In ntfnhandlers.go, create the basic ReorgData type without the full
chains or the common ancestor, and create a reorg signal queue
using a chan ReorgData.
Add ReorgSignaler, which processes ReorgData sent on reorgDataChan (use
queueReorg to send), allowing each package's reorg handler to finish
before proceeding to the next reorg in the queue. This should be
launched as a goroutine after starting the RPC client.
Add queueReorg to queue a new reorg via the reorgDataChan.
Factor out data collection into (*chainMonitor).collect.
Remove the reorganizing flag since the block connected handler does not
help with the reorg anymore.
Blocks that do not connect after the last-stored block should not be
sent to BlockConnectedHandler.  It is not capable of checking that the
blocks it receives are next for dcrdata.  It only only collects data and
calles each BlockDataSaver's Store function.
The first block in the slice is now the lowest after the common
ancestror, while the last is the chain tip.
Use full chain data in (*ChainMonitor).switchToSideChain. No more RPCs.
Remove reorg-related fields of ChainMonitor, except the RWMutex. Just
call switchToSideChain, passing the reorgData from the signal.
Add reorg results checks, ensuring new tip is the expected hash at the
expected height.
Remove the unused BlockConnected(|Sync) functions since they are no
longer used.  Only the ReorgHandler is needed now.
Call switchToSide chain directly from ReorgHandler.
Add checks in switchToSideChain to ensure correct starting DB height.
Use the new and faster GetBestBlockHeightHash instead of
GetBestBlockSummary to get just the height and hash.
Add more checks on the reorg result in ReorgHandler.
Just like dcrsqlite reorg handling.
@chappjc
Copy link
Member Author

chappjc commented Nov 20, 2018

PR description updated with design and implementation details. Will be ready testing by other team members shortly. I will update the description when ready.

Note this PR also bumps the required dcrd JSON RPC version to 5.0.0 (and only 5.0.0). Previously dcrd JSON RPC versions are incompatible! dcrd had it's JSON RPC version bumped to 5.0.0 a few hours ago in PR decred/dcrd#1531... rebuild dcrd from latest master to test!

Add superQueue function and anyQueue (chan interface{}) for queueing
the handling of different types of event notifications, while keeping the
order in which they were received from the daemon. superQueue
manages the event notification queue, keeping the events in the
same order they were received by the rpcclient.NotificationHandlers, and
sending them to the appropriate handlers. It should be a goroutine.
MakeNodeNtfnHandlers launches superQueue, and sends both new block
data and reorg data on the anyQueue channel to superQueue.

Make package level block queue (instance of collectionQueue).
Make package level dcrd RPC client.
Create (*collectionQueue).processBlock for synchronous block processing.
Remove reorgDataChan, queueReorg, and ReorgSignaler.  Instead,
perform reorg synchronously in signalReorg.
In signalReorg, make stakedb reorg run first, so that blockdata and
dcrsqlite will be able to get the ticket pool info for the new blocks.
Allow reorg handling in dcrsqlite, blockdata, and dcrpg to run
concurrently since they only need the stakedb update first.
Set the collectionQueue's previous/best block at the end of the reorg.
CollectHash log can be more informative Debugf
@chappjc chappjc merged commit 6c9148f into decred:master Nov 21, 2018
@chappjc chappjc deleted the reorg-ntfn-fix branch November 21, 2018 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant