Performance - linear searches thru blockchain on checking unpark
In CTransaction::CheckInputs():
If the transaction is an 'unpark', one of the checks is see if the parked transaction can be found in the current trusted blockchain.
This check is done by way of a linear search thru the blockchain.
In 2.1.1 with a limited # of blocks being held in memory, this has the consequence of loading a lot of blocks off-disk and into memory
There could be a network stability / security aspect to this problem if a node is at the edge of memory capacity. An attacker can park a very small number of coins, let them sit parked, and then cause a DoS of sorts "on command" when the coin is unparked. The longer an attacker lets the coins stay parked, the greater impact this unpark would have.
Some of these unpark() transactions have to search 300k+ blocks back to find the park() transaction, causing client memory usage to increase rapidly (in the current block cache implementation)
Similar full searches of the blockchain can be found in other parts of the code.
Options / ideas: 1- implement this check a different way, or remove it 2- perform this search on mapPrev, if the beginning and end block hashes are known 3- keep a database of transactions known to be "in the trusted chain" (and then manage this data set if there are forks, etc) 4- change the block load() algorithm to unload blocks if the in-memory cache exceeds a certain number of blocks ... this will still cause thrashing, but it will limit memory usage 5- implement a different method to search the database without loading blocks into memory
bool CTransaction::CheckInputs()
(main.cpp, around line 1668 for (; pindexPark && pindexPark->nHeight != coins.nHeight; pindexPark = pindexPark->pprev) {};
Example blocks that trigger this behavior:
352941 ff383afb7e6f2aa77fe845c1b7e958d0728813efdef19bf132e55015a98dfee3
401521 1c6e5ab67355457cc9d07a9764bf1e50cff9c875f8a7774a7cbeed18c07f6ef8
Comments (3)
-
Michael Witrant
-
Michael Witrant
3- keep a database of transactions known to be "in the trusted chain" (and then manage this data set if there are forks, etc)
The "coin" database already does that (and that's how the block height is known in this case).
4- change the block load() algorithm to unload blocks if the in-memory cache exceeds a certain number of blocks ... this will still cause thrashing, but it will limit memory usage
That may be a good way to prevent memory exhaustion for processes we have not yet updated. We should log a warning in this case, to know there's something to fix.
5- implement a different method to search the database without loading blocks into memory
There are a few. mapPrev is one. The "next" database is another. In B&C I had to add other things in the database.
-
Michael Witrant
- edited description
- changed milestone to 2.1.1
- Log in to comment
In this case since the exact block height is known you could just use the prev method "height" times: https://bitbucket.org/JordanLeePeershares/nubit/src/548b256675a10042c77a3bf1798ec27289d10c57/src/blockmap.h?at=2.1.1-RC&fileviewer=file-view-default#blockmap.h-115
I tried to remove or update all of them, so there should not be much more left. If that's not the case then we may have to find a more generic solution as you suggested. Do you have other examples?