Permalink
Browse files

make anti-wipeout protection work.

This refactors the BlocksDB memory of the start of the UAHF fork by
storing the CBlockIndex pointer instead of the hash.

Additionally I wrote a unit test for wipeout protection and added the
feature by making connectBlock() aware of our requirement of the bigger
block.

Signed-off-by: TomZ <tomz@freedommail.ch>
  • Loading branch information...
zander committed Jul 30, 2017
1 parent eabcc02 commit 9c0cca4e08559a5f8b09eafd04bbea5dfd3024c9
Showing with 112 additions and 34 deletions.
  1. +24 −20 src/BlocksDB.cpp
  2. +2 −2 src/BlocksDB.h
  3. +3 −1 src/BlocksDB_p.h
  4. +14 −3 src/main.cpp
  5. +69 −8 src/test/uahf_tests.cpp
View
@@ -372,8 +372,15 @@ bool Blocks::DB::CacheAllBlockInfos()
}
if (Application::uahfChainState() != Application::UAHFDisabled) {
Read(DB_UAHF_FORK_BLOCK, d->uahfStartBlock);
setUahfForkBlock(d->uahfStartBlock);
uint256 uahfStartBlockId;
Read(DB_UAHF_FORK_BLOCK, uahfStartBlockId);
if (!uahfStartBlockId.IsNull()) {
auto mi = Blocks::indexMap.find(uahfStartBlockId);
if (mi != Blocks::indexMap.end()) {
d->uahfStartBlock = mi->second;
d->updateUahfProperties();
}
}
if (Application::uahfChainState() != Application::UAHFActive) {
auto bi = chainActive.Tip();
@@ -469,27 +476,23 @@ const std::list<CBlockIndex *> &Blocks::DB::headerChainTips()
return d->headerChainTips;
}
bool Blocks::DB::setUahfForkBlock(const uint256 &blockId)
bool Blocks::DB::setUahfForkBlock(CBlockIndex *index)
{
bool writeOk = true;
if (d->uahfStartBlock != blockId) {
d->uahfStartBlock = blockId;
writeOk = Write(DB_UAHF_FORK_BLOCK, d->uahfStartBlock);
}
assert(index);
d->uahfStartBlock = index;
d->updateUahfProperties();
if (writeOk && !d->uahfStartBlock.IsNull()) {
auto mi = Blocks::indexMap.find(d->uahfStartBlock);
if (mi != Blocks::indexMap.end()) {
const CBlockIndex *bi = mi->second;
assert(bi);
if (bi->pprev && bi->pprev->GetMedianTimePast() >= Application::uahfStartTime())
Application::setUahfChainState(Application::UAHFActive);
}
}
return writeOk;
return Write(DB_UAHF_FORK_BLOCK, d->uahfStartBlock->GetBlockHash());
}
void Blocks::DBPrivate::updateUahfProperties()
{
assert(uahfStartBlock);
if (uahfStartBlock->pprev && uahfStartBlock->pprev->GetMedianTimePast() >= Application::uahfStartTime())
Application::setUahfChainState(Application::UAHFActive);
}
const uint256 &Blocks::DB::uahfForkBlock() const
CBlockIndex *Blocks::DB::uahfForkBlock() const
{
return d->uahfStartBlock;
}
@@ -537,6 +540,7 @@ boost::filesystem::path Blocks::getFilepathForIndex(int fileIndex, const char *p
///////////////////////////////////////////////
Blocks::DBPrivate::DBPrivate()
: isReindexing(false)
: isReindexing(false),
uahfStartBlock(nullptr)
{
}
View
@@ -93,8 +93,8 @@ class DB : public CDBWrapper
const CChain &headerChain();
const std::list<CBlockIndex*> & headerChainTips();
const uint256 &uahfForkBlock() const;
bool setUahfForkBlock(const uint256 &blockId);
CBlockIndex *uahfForkBlock() const;
bool setUahfForkBlock(CBlockIndex *index);
private:
static DB *s_instance;
View
@@ -31,11 +31,13 @@ class DBPrivate {
public:
DBPrivate();
void updateUahfProperties();
bool isReindexing;
CChain headersChain;
std::list<CBlockIndex*> headerChainTips;
uint256 uahfStartBlock;
CBlockIndex *uahfStartBlock;
};
}
View
@@ -2219,7 +2219,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
} else if (Application::uahfChainState() == Application::UAHFRulesActive && pindex->pprev->GetMedianTimePast() >= Application::uahfStartTime()) {
logInfo(8002) << "UAHF block found that activates the chain" << block.GetHash();
// enable UAHF (aka BCC) on first block after the calculated timestamp
Blocks::DB::instance()->setUahfForkBlock(block.GetHash()); // this will update Application::uahfChainState
Blocks::DB::instance()->setUahfForkBlock(pindex); // this will update Application::uahfChainState
}
@@ -2477,6 +2477,17 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
return AbortNode(state, "Failed to read block");
pblock = &block;
}
CBlockIndex *uahfForkBlock = Blocks::DB::instance()->uahfForkBlock();
if (uahfForkBlock && uahfForkBlock->nHeight == pindexNew->nHeight) { // this is a new potential fork-block.
// The uahf fork-block has to be larger than 1MB.
const uint32_t minBlockSize = Params().GenesisBlock().nTime == Application::uahfStartTime() // no bigger block in default regtest setup.
&& Params().NetworkIDString() == CBaseChainParams::REGTEST ? 0 : MAX_BLOCK_SIZE + 1;
const std::uint32_t blockSize = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
if (blockSize < minBlockSize)
return false;
}
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
@@ -3165,8 +3176,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// If UAHF is enabled for the curent block, but not for the previous
// block, we must check that the block is larger than 1MB.
const uint32_t minBlockSize = Params().GenesisBlock().nTime == Application::uahfStartTime() // no bigger block in default regtest setup.
&& Params().NetworkIDString() == CBaseChainParams::REGTEST ? 0 : MAX_BLOCK_SIZE;
if (blockSize <= minBlockSize)
&& Params().NetworkIDString() == CBaseChainParams::REGTEST ? 0 : MAX_BLOCK_SIZE + 1;
if (blockSize < minBlockSize)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-too-small", false, "size limits failed");
}
View
@@ -25,6 +25,7 @@
#include <chainparams.h>
#include <primitives/transaction.h>
#include <main.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <vector>
@@ -51,12 +52,14 @@ static CBlockIndex *createBlockIndex(CBlockIndex *prev, int height, int time, ui
return index;
}
static CBlock createBlock(CBlockIndex *parent, const std::vector<CTransaction>& txns)
static CBlock createBlock(CBlockIndex *parent, const std::vector<CTransaction>& txns, const std::vector<unsigned char> &msg = std::vector<unsigned char>())
{
CMutableTransaction coinbase;
coinbase.vin.resize(1);
coinbase.vout.resize(1);
coinbase.vin[0].scriptSig = CScript() << (parent->nHeight + 1) << OP_0;
if (!msg.empty())
coinbase.vin[0].scriptSig << msg;
coinbase.vout[0].nValue = 50 * COIN;
CBlock block;
@@ -72,6 +75,12 @@ static CBlock createBlock(CBlockIndex *parent, const std::vector<CTransaction>&
block.vtx.push_back(tx);
}
// make it actually valid
block.hashMerkleRoot = BlockMerkleRoot(block);
do {
++block.nNonce;
} while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus()));
return block;
}
@@ -100,7 +109,7 @@ BOOST_AUTO_TEST_CASE(Test_Enabling)
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFWaiting);
BOOST_CHECK_EQUAL(Application::uahfStartTime(), 12352);
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == uint256());
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == nullptr);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFWaiting);
// we use the MTP, which uses 11 blocks, so make sure we actually have those
@@ -115,20 +124,22 @@ BOOST_AUTO_TEST_CASE(Test_Enabling)
chainActive.SetTip(tip);
// tip GMTP is 20600, the one before 20500
Blocks::DB::instance()->setUahfForkBlock(hashes[11]);
Blocks::DB::instance()->setUahfForkBlock(tip);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFActive);
BOOST_CHECK(hashes[11] == tip->GetBlockHash());
mapArgs["-uahfstarttime"] = "0";
MockApplication::doInit();
Blocks::DB::createInstance(0, false);
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == uint256());
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == nullptr);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFDisabled);
mapArgs["-uahfstarttime"] = "12352";
MockApplication::doInit();
Blocks::DB::createInstance(0, false);
Blocks::DB::instance()->CacheAllBlockInfos();
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == hashes[11]);
logDebug() << Blocks::DB::instance()->uahfForkBlock()->GetBlockHash() << hashes[11];
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock()->GetBlockHash() == hashes[11]);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFActive);
/* UAHF spec states;
@@ -145,23 +156,23 @@ BOOST_AUTO_TEST_CASE(Test_Enabling)
MockApplication::doInit();
Blocks::DB::createInstance(0, false);
Blocks::DB::instance()->CacheAllBlockInfos();
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == hashes[11]);
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock()->GetBlockHash() == hashes[11]);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFActive);
// Defining UAHF starts at 20600 means the tip is the last one before the fork block.
mapArgs["-uahfstarttime"] = "20600";
MockApplication::doInit();
Blocks::DB::createInstance(0, false);
Blocks::DB::instance()->CacheAllBlockInfos();
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == hashes[11]);
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock()->GetBlockHash() == hashes[11]);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFRulesActive);
// Check for off-by-one sec
mapArgs["-uahfstarttime"] = "20601";
MockApplication::doInit();
Blocks::DB::createInstance(0, false);
Blocks::DB::instance()->CacheAllBlockInfos();
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock() == hashes[11]);
BOOST_CHECK(Blocks::DB::instance()->uahfForkBlock()->GetBlockHash() == hashes[11]);
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFWaiting);
}
@@ -291,4 +302,54 @@ BOOST_AUTO_TEST_CASE(Test_isCommitment) {
BOOST_CHECK(s.isCommitment(params.antiReplayOpReturnCommitment));
}
BOOST_AUTO_TEST_CASE(Test_rollbackProtection)
{
// create 20 block.
CBlockIndex *tip = chainActive.Tip();
BOOST_CHECK_EQUAL(tip->nHeight, 0);
mapArgs["-uahfstarttime"] = "0"; // turn off UAHF
MockApplication::doInit();
std::vector<CTransaction> transactions;
for (int i = 0; i < 20; ++i) {
CBlock block = createBlock(tip, transactions);
CValidationState state;
ProcessNewBlock(state, Params(), NULL, &block, true, NULL);
auto it = Blocks::indexMap.find(block.GetHash());
BOOST_CHECK(it != Blocks::indexMap.end());
if (it != Blocks::indexMap.end()) {
tip = it->second;
} else {
break;
}
}
BOOST_CHECK_EQUAL(chainActive.Height(), 20);
mapArgs["-uahfstarttime"] = "1296688702";
MockApplication::doInit();
Blocks::DB::instance()->setUahfForkBlock(tip); // pretend our last one was the fork-block.
BOOST_CHECK_EQUAL(Application::uahfChainState(), Application::UAHFActive);
std::vector<unsigned char> message;// to avoid the same identical blocks being mined, add a message.
message.push_back('x');
// now create a chain-split. Small blocks from before the activation block.
tip = chainActive[17];
for (int i = 0; i < 10; ++i) {
CBlock block = createBlock(tip, transactions, message);
CValidationState state;
ProcessNewBlock(state, Params(), NULL, &block, true, NULL);
auto it = Blocks::indexMap.find(block.GetHash());
if (it != Blocks::indexMap.end()) {
tip = it->second;
} else {
break;
}
}
// we should not have had a re-org
BOOST_CHECK_EQUAL(chainActive.Height(), 20);
}
BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 9c0cca4

Please sign in to comment.