Files

434 lines
14 KiB
C++
Raw Permalink Normal View History

2017-04-03 15:30:03 +02:00
/*
2017-11-09 19:34:51 +01:00
* This file is part of the Flowee project
2021-06-20 22:44:44 +02:00
* Copyright (C) 2017-2020 Tom Zander <tom@flowee.org>
2017-04-03 15:30:03 +02:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2020-08-16 11:10:43 +02:00
#include "blocksdb_tests.h"
2017-04-03 15:30:03 +02:00
#include <BlocksDB.h>
#include <primitives/Block.h>
2017-04-03 15:30:03 +02:00
#include <chain.h>
static bool contains(const std::list<CBlockIndex*> &haystack, CBlockIndex *needle)
{
// and the stl designers wonder why people say C++ is too verbose and hard to use.
// would it really have hurt us if they had a std::list<T>::contains(T t) method?
return (std::find(haystack.begin(), haystack.end(), needle) != haystack.end());
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::headersChain()
2017-04-03 15:30:03 +02:00
{
uint256 dummyHash;
CBlockIndex root;
root.nHeight = 0;
root.phashBlock = &dummyHash;
CBlockIndex b1;
b1.nChainWork = 0x10;
b1.nHeight = 1;
b1.pprev = &root;
b1.phashBlock = &dummyHash;
CBlockIndex b2;
b2.pprev = &b1;
b2.nHeight = 2;
b2.nChainWork = 0x20;
b2.phashBlock = &dummyHash;
CBlockIndex b3;
b3.pprev = &b2;
b3.nHeight = 3;
b3.nChainWork = 0x30;
b3.phashBlock = &dummyHash;
CBlockIndex b4;
b4.pprev = &b3;
b4.nHeight = 4;
b4.nChainWork = 0x40;
b4.phashBlock = &dummyHash;
CBlockIndex bp3;
bp3.pprev = &b2;
bp3.nHeight = 3;
bp3.nChainWork = 0x31;
bp3.phashBlock = &dummyHash;
CBlockIndex bp4;
bp4.pprev = &bp3;
bp4.nHeight = 4;
bp4.nChainWork = 0x41;
bp4.phashBlock = &dummyHash;
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
bool changed = db->appendHeader(&root);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &root);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &root);
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&b1);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &b1);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &b1);
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&b4);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &b4);
QCOMPARE(db->headerChain().Height(), 4);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &b4);
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&bp3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, false);
QCOMPARE(db->headerChain().Tip(), &b4);
QCOMPARE(db->headerChain().Height(), 4);
QCOMPARE(db->headerChainTips().size(), 2);
QVERIFY(contains(db->headerChainTips(), &b4));
QVERIFY(contains(db->headerChainTips(), &bp3));
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&bp4);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &bp4);
QCOMPARE(db->headerChain().Height(), 4);
QCOMPARE(db->headerChainTips().size(), 2);
QVERIFY(contains(db->headerChainTips(), &b4));
QVERIFY(contains(db->headerChainTips(), &bp4));
2017-04-03 15:30:03 +02:00
2020-08-16 11:10:43 +02:00
QCOMPARE(db->headerChain()[0], &root);
QCOMPARE(db->headerChain()[1], &b1);
QCOMPARE(db->headerChain()[2], &b2);
QCOMPARE(db->headerChain()[3], &bp3);
QCOMPARE(db->headerChain()[4], &bp4);
2017-04-03 15:30:03 +02:00
}
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
bool changed = db->appendHeader(&bp3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &bp3);
QCOMPARE(db->headerChain().Height(), 3);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &bp3);
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&b3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, false);
QCOMPARE(db->headerChain().Tip(), &bp3);
QCOMPARE(db->headerChain().Height(), 3);
QCOMPARE(db->headerChainTips().size(), 2);
QVERIFY(contains(db->headerChainTips(), &bp3));
QVERIFY(contains(db->headerChainTips(), &b3));
2017-04-03 15:30:03 +02:00
2020-08-16 11:10:43 +02:00
QCOMPARE(db->headerChain()[0], &root);
QCOMPARE(db->headerChain()[1], &b1);
QCOMPARE(db->headerChain()[2], &b2);
QCOMPARE(db->headerChain()[3], &bp3);
2017-04-03 15:30:03 +02:00
}
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
bool changed = db->appendHeader(&b3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
2017-04-03 15:30:03 +02:00
changed = db->appendHeader(&b2);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, false);
QCOMPARE(db->headerChain().Tip(), &b3);
QCOMPARE(db->headerChain().Height(), 3);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &b3);
2017-04-03 15:30:03 +02:00
}
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
2022-02-22 17:28:33 +01:00
/*bool changed =*/ db->appendHeader(&root);
db->appendHeader(&b1);
db->appendHeader(&b2);
db->appendHeader(&b3);
2017-04-03 15:30:03 +02:00
bp3.nChainWork = b3.nChainWork;
2022-02-22 17:28:33 +01:00
bool changed = db->appendHeader(&bp3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, false);
QCOMPARE(db->headerChain().Tip(), &b3);
QCOMPARE(db->headerChain().Height(), 3);
QCOMPARE(db->headerChainTips().size(), 2);
2017-04-03 15:30:03 +02:00
}
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::headersChain2()
2017-04-03 15:30:03 +02:00
{
uint256 dummyHash;
CBlockIndex root;
root.nHeight = 0;
root.phashBlock = &dummyHash;
CBlockIndex b1;
b1.nChainWork = 0x10;
b1.nHeight = 1;
b1.pprev = &root;
b1.phashBlock = &dummyHash;
CBlockIndex b2;
b2.pprev = &b1;
b2.nHeight = 2;
b2.nChainWork = 0x20;
b2.phashBlock = &dummyHash;
CBlockIndex b3;
b3.pprev = &b2;
b3.nHeight = 3;
b3.nChainWork = 0x30;
b3.phashBlock = &dummyHash;
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
2022-02-22 17:28:33 +01:00
/*bool changed =*/ db->appendHeader(&root);
db->appendHeader(&b1);
db->appendHeader(&b2);
db->appendHeader(&b3);
2017-04-03 15:30:03 +02:00
b3.nStatus |= BLOCK_FAILED_VALID;
2022-02-22 17:28:33 +01:00
bool changed = db->appendHeader(&b3);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &b2);
QCOMPARE(db->headerChain().Height(), 2);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &b2);
2017-04-03 15:30:03 +02:00
}
b3.nStatus = 0;
{
Blocks::DB::createTestInstance(100);
Blocks::DB *db = Blocks::DB::instance();
2022-02-22 17:28:33 +01:00
/*bool changed =*/ db->appendHeader(&root);
db->appendHeader(&b1);
db->appendHeader(&b2);
db->appendHeader(&b3);
2017-04-03 15:30:03 +02:00
b2.nStatus |= BLOCK_FAILED_VALID;
2022-02-22 17:28:33 +01:00
bool changed = db->appendHeader(&b2);
2020-08-16 11:10:43 +02:00
QCOMPARE(changed, true);
QCOMPARE(db->headerChain().Tip(), &b1);
QCOMPARE(db->headerChain().Height(), 1);
QCOMPARE(db->headerChainTips().size(), 1);
QCOMPARE(db->headerChainTips().front(), &b1);
2017-04-03 15:30:03 +02:00
}
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::invalidate()
2018-01-15 15:26:12 +00:00
{
// create a chain of 20 blocks.
2021-11-02 10:18:24 +01:00
std::vector<Block> blocks = bv->appendChain(20);
2018-01-15 15:26:12 +00:00
// split the chain so we have two header-chain-tips
CBlockIndex *b18 = Blocks::Index::get(blocks.at(18).createHash());
2020-08-16 11:10:43 +02:00
auto block = bv->createBlock(b18);
bv->addBlock(block, 0).start().waitUntilFinished();
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 2);
2018-01-15 15:26:12 +00:00
// then invalidate a block in the common history of both chains
CBlockIndex *b14 = Blocks::Index::get(blocks.at(14).createHash());
2020-08-16 11:10:43 +02:00
QVERIFY(b14);
2018-01-15 15:26:12 +00:00
b14->nStatus |= BLOCK_FAILED_VALID;
bool changed = Blocks::DB::instance()->appendHeader(b14);
2020-08-16 11:10:43 +02:00
QVERIFY(changed);
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b14->pprev);
2018-01-15 15:26:12 +00:00
for (auto tip : Blocks::DB::instance()->headerChainTips()) {
2020-08-16 11:10:43 +02:00
QCOMPARE(tip, b14->pprev);
2018-01-15 15:26:12 +00:00
}
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 1);
2018-01-15 15:26:12 +00:00
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::invalidate2()
2018-01-15 15:26:12 +00:00
{
/*
* x b8 b9
* \
* b9b b10b
*
* Invalidating 'b9b' should remove the second branch with b10
*/
2021-11-02 10:18:24 +01:00
std::vector<Block> blocks = bv->appendChain(10);
2018-01-15 15:26:12 +00:00
// split the chain so we have two header-chain-tips
CBlockIndex *b9 = Blocks::Index::get(blocks.at(9).createHash()); // chain-tip
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b9);
2018-01-15 15:26:12 +00:00
CBlockIndex *b8 = Blocks::Index::get(blocks.at(8).createHash());
2020-08-16 11:10:43 +02:00
auto block = bv->createBlock(b8);
bv->addBlock(block, 0).start().waitUntilFinished();
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 2);
2018-01-15 15:26:12 +00:00
CBlockIndex *b9b = Blocks::Index::get(block.createHash());
2020-08-16 11:10:43 +02:00
block = bv->createBlock(b9b); // new chain-tip
bv->addBlock(block, 0).start().waitUntilFinished();
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 2);
2018-01-15 15:26:12 +00:00
CBlockIndex *b10b = Blocks::Index::get(block.createHash());
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b10b);
2018-01-15 15:26:12 +00:00
// then invalidate block b9b
2018-01-15 15:26:12 +00:00
b9b->nStatus |= BLOCK_FAILED_VALID;
bool changed = Blocks::DB::instance()->appendHeader(b9b);
2020-08-16 11:10:43 +02:00
QVERIFY(changed);
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b9);
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 1);
2018-01-15 15:26:12 +00:00
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::invalidate3()
{
/*
* b6 b7 b8 b9
* \
* b7` b8` b9` b10`
*
* Create competing chain until reorg.
* Then invalidate b8` and check if we go back to b9
*/
2021-11-02 10:18:24 +01:00
std::vector<Block> blocks = bv->appendChain(10);
// split the chain so we have two header-chain-tips
const CBlockIndex *b9 = Blocks::Index::get(blocks.at(9).createHash()); // chain-tip
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b9);
CBlockIndex *b6 = Blocks::Index::get(blocks.at(6).createHash());
CBlockIndex *b8b = nullptr;
CBlockIndex *parent = b6;
for (int i = 0; i < 4; ++i) {
2020-08-16 11:10:43 +02:00
auto block = bv->createBlock(parent);
bv->addBlock(block, 0).start().waitUntilFinished();
parent = Blocks::Index::get(block.createHash());
if (parent->nHeight == 9)
b8b = parent;
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 2);
}
2020-08-16 11:10:43 +02:00
QCOMPARE(parent->nHeight, 11);
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), parent);
assert(b8b);
2020-08-16 11:10:43 +02:00
QCOMPARE(b8b->nHeight, 9);
QCOMPARE(b8b->pprev->pprev, b6);
b8b->nStatus |= BLOCK_FAILED_VALID;
bool changed = Blocks::DB::instance()->appendHeader(b8b);
2020-08-16 11:10:43 +02:00
QVERIFY(changed);
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), b9);
QCOMPARE(Blocks::DB::instance()->headerChainTips().size(), 2);
}
2020-08-16 11:10:43 +02:00
void TestBlocksDB::addImpliedInvalid()
2018-01-15 15:26:12 +00:00
{
/*
* Starting with;
* x x x x
* And then adding an item a3 that would create;
* x x x x a1 a2 a3
* requires me to check all new items for validity, to see if they have been marked as failing.
* If one is failing, then all are.
*/
2022-02-22 17:28:33 +01:00
/*std::vector<Block> blocks = */ bv->appendChain(10);
2018-01-15 15:26:12 +00:00
auto * const x = Blocks::DB::instance()->headerChain().Tip();
2020-08-16 11:10:43 +02:00
QCOMPARE(x->nHeight, 10);
2018-01-15 15:26:12 +00:00
uint256 hashes[3];
CBlockIndex a1;
a1.nHeight = 11;
a1.pprev = x;
a1.phashBlock = &hashes[0];
a1.nChainWork = x->nChainWork + 0x10;
a1.nStatus = BLOCK_FAILED_VALID;
CBlockIndex a2;
a2.nChainWork = a1.nChainWork + 0x10;
a2.nHeight = a1.nHeight + 1;
a2.pprev = &a1;
a2.phashBlock = &hashes[1];
a2.nStatus = BLOCK_FAILED_CHILD;
CBlockIndex a3;
a3.nChainWork = a2.nChainWork + 0x10;
a3.nHeight = a2.nHeight + 1;
a3.pprev = &a2;
a3.phashBlock = &hashes[2];
a3.nStatus = BLOCK_FAILED_CHILD;
Blocks::DB::instance()->appendHeader(&a3);
2020-08-16 11:10:43 +02:00
QCOMPARE(Blocks::DB::instance()->headerChain().Tip(), x);
2018-01-15 15:26:12 +00:00
}
2022-02-22 17:35:33 +01:00
#include <BlocksDB_p.h> // for vInfoBlockFile
2022-02-22 17:35:33 +01:00
void TestBlocksDB::mapFileWrite()
{
// There likely is one already, for the genesis block, lets avoid interacting with it and just force a new file.
QCOMPARE(vinfoBlockFile.size(), 1);
vinfoBlockFile[0].nSize = MAX_BLOCKFILE_SIZE - 107;
Blocks::DB *db = Blocks::DB::instance();
Streaming::BufferPool pool;
pool.reserve(100);
for (int i = 0; i < 100; ++i) {
pool.begin()[i] = static_cast<char>(i);
}
Block block(pool.commit(100));
QCOMPARE(block.size(), 100);
QCOMPARE(block.blockVersion(), 0x3020100);
CDiskBlockPos pos;
{
Block newBlock = db->writeBlock(block, pos);
QCOMPARE(newBlock.blockVersion(), 0x3020100);
QCOMPARE(newBlock.size(), 100);
QCOMPARE(pos.nFile, 1);
QCOMPARE(pos.nPos, 8);
}
{
Block block2 = db->loadBlock(CDiskBlockPos(1, 8));
QCOMPARE(block2.size(), 100);
QCOMPARE(block2.blockVersion(), 0x3020100);
}
// add a second block
pool.reserve(120);
for (int i = 0; i < 120; ++i) {
pool.begin()[i] = static_cast<char>(i + 1);
}
Block block2(pool.commit(120));
QCOMPARE(block2.size(), 120);
QCOMPARE(block2.blockVersion(), 0x4030201);
{
Block newBlock = db->writeBlock(block2, pos);
QCOMPARE(newBlock.size(), 120);
QCOMPARE(pos.nFile, 1);
QCOMPARE(pos.nPos, 116);
QCOMPARE(newBlock.blockVersion(), 0x4030201);
}
{
Block block3 = db->loadBlock(CDiskBlockPos(1, 8));
QCOMPARE(block3.size(), 100);
QCOMPARE(block3.blockVersion(), 0x3020100);
QCOMPARE(block3.data().begin()[99], (char) 99);
Block block4 = db->loadBlock(CDiskBlockPos(1, 116));
QCOMPARE(block4.size(), 120);
QCOMPARE(block4.blockVersion(), 0x4030201);
QVERIFY(block4.data().begin()[119] == 120);
}
}