`getblock` is slow on verbosity, it's happened because `LOCK(cs_main)` applied for whole function including JSON generation
https://github.com/bitcoin/bitcoin/blob/ef70f9b52b851c7997a9f1a0834714e3eebc1fd8/src/rpc/blockchain.cpp#L810

I added few lines which print spent time:
```cpp
    LOCK(cs_main);
    auto t1 = std::chrono::high_resolution_clock::now();
...
    auto t2 = std::chrono::high_resolution_clock::now();
    auto ret = blockToJSON(block, pblockindex, verbosity >= 2);
    auto t3 = std::chrono::high_resolution_clock::now();
    std::cout << "blockToJSON " << pblockindex->nHeight << ": " << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << " " << std::chrono::duration_cast<std::chrono::microseconds>(t3 - t1).count() << std::endl;
    return ret;
```

and numbers looks like:
```
blockToJSON 570019: 14727 201870
blockToJSON 570022: 16049 225224
blockToJSON 570021: 33776 217398
blockToJSON 570020: 21754 191669
blockToJSON 570023: 30949 236941
blockToJSON 570025: 23346 223673
blockToJSON 570024: 18732 203002
blockToJSON 570026: 17467 216213
blockToJSON 570027: 16658 217395
blockToJSON 570028: 19015 219183
```

i.e. everything before JSON generation takes 15-30ms, while JSON takes minimum 190ms and lock `cs_main` for all this time. As result it's possible extract only ~5 blocks per second.

Looking on the code I think it's possible copy data from `CBlockIndex` to extra structure, so in this case `cs_main` can be unlocked right after `GetBlockChecked`.
https://github.com/bitcoin/bitcoin/blob/ef70f9b52b851c7997a9f1a0834714e3eebc1fd8/src/rpc/blockchain.cpp#L828
As result `getblock` in verbosity mode should be much faster (in case if `-rpcthreads` will be adjusted too).

Does this make sense?
"    \"status\": \"xxxx\"        (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n"
            "  }\n"
            "]\n"
            "Possible values for status:\n"
            "1.  \"invalid\"               This branch contains at least one invalid block\n"
            "2.  \"headers-only\"          Not all blocks for this branch are available, but the headers are valid\n"
            "3.  \"valid-headers\"         All blocks are available for this branch, but they were never fully validated\n"
            "4.  \"valid-fork\"            This branch is not part of the active chain, but is fully validated\n"
            "5.  \"active\"                This is the tip of the active main chain, which is certainly valid\n"
                },
                RPCExamples{
                    HelpExampleCli("getchaintips", "")
            + HelpExampleRpc("getchaintips", "")
                },
            }.ToString());
    LOCK(cs_main);
    /*
     * Idea:  the set of chain tips is chainActive.tip, plus orphan blocks which do not have another orphan building off of them.
     * Algorithm:
     *  - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
     *  - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
     *  - add chainActive.Tip()
     */
    std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
    std::set<const CBlockIndex*> setOrphans;
    std::set<const CBlockIndex*> setPrevs;
    for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
    {
        if (!chainActive.Contains(item.second)) {
            setOrphans.insert(item.second);