OK, I looked into this, and blkindex.dat stores both the index to the blocks and the index to the transactions + a few other things (!!!). Here's the relevant code that records a block into the index:
bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
{
return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
}
In other words, the key is whatever pair
bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
{
return Write(string("hashBestChain"), hashBestChain);
}
bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
{
return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
}
bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
{
assert(!fClient);
// Add to tx index
uint256 hash = tx.GetHash();
CTxIndex txindex(pos, tx.vout.size());
return Write(make_pair(string("tx"), hash), txindex);
}