What was the timeline of the Bitcoin Unlimited Hack as of 2017-March and where was it in the code? I've been trying to follow the Bitcoin Unlimited hack timeline (2017-Mar) and there seems to be conflicting stories on both sides. It's obviously to confuse the 3rd party into finding the truth. My main questions are:


When was the exploit discovered and by whom?
When was the fix issued after the discovery of the exploit?
When did the hack occur to knock out over 700+ Bitcoin Unlimited nodes?
There were reports of these 700+ nodes going offline and then within a few hours, it all came back online. If this is the case, how does one conclude that these 700+ nodes are controlled by a few people?
Who are the main characters on both sides in this hack to affect news stories and perception?
Where was the hack in the code exactly?
The commit implementing the fix was issued Mar 14, 2017, 3:16 PM GMT. A Github release was made at 7:39 PM GMT. It was announced on Reddit at 7:59 PM GMT. The vulnerability was discovered sometime before the git commit, but it's unclear how long before.

According to nodecounter, things were fine at about 6 PM GMT, when there were 776 nodes. That rapidly dropped, with 696 nodes at 7 PM GMT. It bottomed out at 11 PM, with 182 nodes. At 9 AM GMT the following day, things were mostly back to normal, with 626 nodes. I don't think it's very accurate to say that BU reacted abnormally quickly, or that 100% of nodes came back.


  Where was the hack in the code exactly?


There were two unrelated issues. Both issues were in how Bitcoin Unlimited responds to network packets asking for thinblocks. 


Reachable assert.

BU had this code:

void SendXThinBlock(CBlock &amp;block, CNode* pfrom, const CInv &amp;inv)
{
    if (inv.type == MSG_XTHINBLOCK)
    {
        // code
    }
    else if (inv.type == MSG_THINBLOCK)
    {
        // code code code
    }
    else
    {
        assert(0);
    }
    // more code
}


(I've removed irrelevant parts to make it issue easier to see.)

This means, 


If the request is a thinblock, reply in this way,
if the request is an xthinblock, reply in that way,
if it's neither, crash.


Why does it do that? I assume that the person who wrote this code assumed that this function would only be called for one of those two types of requests. This function is called in two places. In the first place where this function is called, that assumption is correct. In the second place, it is wrong. This bug was introduced May 2016 in this commit.

The fix is to DoS ban the node that sent the request, rather than crashing.
Unchecked return value

BU had this code:

BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
// code code
if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
    assert(!"cannot load block from disk");


The problem here is that if inv.hash does not exist in mapBlockIndex, the first line will return mapBlockIndex.end(). The next line will then pass a null pointer to ReadBlockFromDisk. Then, the node will crash. This bug was introduced Jan 2016 in this commit.