Skip to content

Latest commit

 

History

History
122 lines (101 loc) · 5.98 KB

033.md

File metadata and controls

122 lines (101 loc) · 5.98 KB
BUIP033: Parallel Validation
Proposer: Peter Tschipper
Submitted: 2016-10-22
Status: passed

''Summary:*

Essentially Parallel Validation is a simple concept. Rather than validating each block within the main processing thread, we instead create a separate thread to do the block validation. If more than one block arrives to be processed then we create yet another thread. There are currently up to 4 parallel block processing threads available making a big block DDOS attack impossible. Furthermore, if any attacker were somehow able to jam all 4 processing threads and another block arrived, then the processing for the largest block would be interrupted allowing the smaller block to proceed, unless the larger block or blocks have most proof of work. So only the most proof of work and smallest blocks will be allowed to finish in such
as case.

If there are multiple blocks processing at the same time, when one of the blocks wins the race to complete, then the other threads of processing are interrupted and the winner will be able to update the UTXO and advance the chain tip. Although the other blocks that were interrupted will still be stored on disk in the event of a re-org.

Design:

The following describes the internal mechanisms used to achieve parallel validation.

2a) Script Check Queues: A total of four script check queues are created with their own thread group which are used to validated signatures. Each new block that arrives will be assigned one of those queues during the validation process.

2b) Semaphores: There are two semaphores for managing block validations which are sized at 4, for new blocks, and 32 for IBD. Although there are only 4 script queues available we still allow 32 IBD threads to run concurrently since that gives the main processing thread a chance to retrieve other blocks while the current ones complete their validation.

2c) Locking: The parallel processing is made possible by first having separate threads for validation, but then largely by managing the internal locks of `cs_main` in the `ConnectBlock()` function which is found in the `src/main.cpp` source file. During the process of `CheckInputs()` we do not have to maintain the lock on `cs_main` which allows other competing threads to continue their own validation; the reason for this is that each thread of validation uses it's own view of the UTXO and the scriptcheckqueue's have their own internal locking mechanism. Furthermore when it comes time to wait for the script threads to finish we also do not need to maintain the `cs_main` locks during the `control.Wait()`.

Although this unlocking and locking of `cs_main` causes some overhead it is not invoked during the mining process but only when we receive a new block from an external source which needs validation. It is designed primarily to prevent the big block DDOS attack from jamming the main processing thread.

2d) Interrupts: If multiple blocks are competing to win the validation race or if all 4 script queues are in use and another new block arrives there are several ways we use to stop one or all of the other competing threads. We do this by first sending a message to quit the script threads which prevents them from completing their verification, followed by issuing a standard thread interrupt. Also, if one block has finished and has advanced the tip, the other concurrent threads will see that the tip has advanced and will exit their validation threads.

2e) Temp view cache: Each processing thread has it's own temporary view of the UTXO which it can used to pre-validate the inputs and ensure that the block is valid (as each input is checked the UTXO must be updated before the next input can be checked because many times the current input depends on some previous input in the same block). When and If a processing thread wins the validation race it will flush it's
temporary and now updated view of the UTXO to the base view which then updates the UTXO on disk. This is key to having several threads of validation running concurrently, since we can not have multiple threads all updating the same UTXO base view at the same time.

2f) nSequenceId: In order to have the correct `pindexMostWork` we must update the `nSequenceId` an additional time after the winning block updates the `UTXO` and advances the chain tip. We can not only rely only on the `pindexMostWork` returned from the `CBlockIndexWorkComparator()` as was previously the case. That is because pindexMostWork returned from the comparator may not necessarily point to the winning block. So what we have to do is swap the `nSequenceId` between the winning and losing blocks such that the winning block has the lowest nSequenceId and the losing blocks nSequenceId's are bumped up one while at the same time keeping their relative ordering.

*IBD and new blocks:''

Parallel Validation is only in effect when new blocks arrive. In other words, the `fIsChainNearlySyncd` flag must be true indicating that IBD has been completed.

Although parallel validation is not in operation during IBD, the blocks arriving that need validating will get processed in a separate thread. This helps to free up the main processing thread and allows requests for more blocks to be made while the other threads continue to be processed.

How is mining affected:

Mining is not affected by Parallel Validation. When new blocks are created locally they bypass parallel validation. In other words, the `cs_main` locks are not unlocked and then locked repeatedly, allowing the validation process to be completed as quickly as possible. Whether parallel validation is invoked or not depends on the boolean `fParallel`. When set to `true` then parallel validation is in effect, and when `false` , as in the case of generating a new block, then it is turned off.

NOTE: Miners will still use parallel validation if a block arrives from an external source. It is only turned off when validating a block they mine themselves.