// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see .
#include
#include "gtest/gtest.h"
#include "CryptoNoteCore/CryptoNoteBasic.h"
#include "CryptoNoteCore/UpgradeDetector.h"
#include "Logging/ConsoleLogger.h"
namespace {
using CryptoNote::BLOCK_MAJOR_VERSION_1;
using CryptoNote::BLOCK_MAJOR_VERSION_2;
using CryptoNote::BLOCK_MINOR_VERSION_0;
using CryptoNote::BLOCK_MINOR_VERSION_1;
struct BlockEx {
CryptoNote::Block bl;
};
typedef std::vector BlockVector;
typedef CryptoNote::BasicUpgradeDetector UpgradeDetector;
class UpgradeTest : public ::testing::Test {
public:
CryptoNote::Currency createCurrency(uint64_t upgradeHeight = UpgradeDetector::UNDEF_HEIGHT) {
CryptoNote::CurrencyBuilder currencyBuilder(logger);
currencyBuilder.upgradeVotingThreshold(90);
currencyBuilder.upgradeVotingWindow(720);
currencyBuilder.upgradeWindow(720);
currencyBuilder.upgradeHeight(upgradeHeight);
return currencyBuilder.currency();
}
protected:
Logging::ConsoleLogger logger;
};
class UpgradeDetector_voting_init : public UpgradeTest {};
class UpgradeDetector_upgradeHeight_init : public UpgradeTest {};
class UpgradeDetector_voting : public UpgradeTest {};
void createBlocks(BlockVector& blockchain, size_t count, uint8_t majorVersion, uint8_t minorVersion) {
for (size_t i = 0; i < count; ++i) {
BlockEx b;
b.bl.majorVersion = majorVersion;
b.bl.minorVersion = minorVersion;
b.bl.timestamp = 0;
blockchain.push_back(b);
}
}
void createBlocks(BlockVector& blockchain, UpgradeDetector& upgradeDetector, size_t count, uint8_t majorVersion, uint8_t minorVersion) {
for (size_t i = 0; i < count; ++i) {
BlockEx b;
b.bl.majorVersion = majorVersion;
b.bl.minorVersion = minorVersion;
b.bl.timestamp = 0;
blockchain.push_back(b);
upgradeDetector.blockPushed();
}
}
void popBlocks(BlockVector& blockchain, UpgradeDetector& upgradeDetector, size_t count) {
for (size_t i = 0; i < count; ++i) {
blockchain.pop_back();
upgradeDetector.blockPopped();
}
}
TEST_F(UpgradeDetector_voting_init, handlesEmptyBlockchain) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_voting_init, votingIsNotCompleteDueShortBlockchain) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfBlocks) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1);
}
TEST_F(UpgradeDetector_voting_init, votingIsNotCompleteDueLackOfVoices) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, currency.minNumberVotingBlocks() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfVoices) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1);
}
TEST_F(UpgradeDetector_voting_init, handlesOneCompleteUpgrade) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
uint64_t upgradeHeight = currency.calculateUpgradeHeight(blocks.size() - 1);
createBlocks(blocks, upgradeHeight - blocks.size(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
// Upgrade is here
createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1);
ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight);
}
TEST_F(UpgradeDetector_voting_init, handlesAFewCompleteUpgrades) {
CryptoNote::Currency currency = createCurrency();
const uint8_t BLOCK_V3 = BLOCK_MAJOR_VERSION_2 + 1;
const uint8_t BLOCK_V4 = BLOCK_MAJOR_VERSION_2 + 2;
BlockVector blocks;
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
uint64_t votingCompleteHeigntV2 = blocks.size() - 1;
uint64_t upgradeHeightV2 = currency.calculateUpgradeHeight(votingCompleteHeigntV2);
createBlocks(blocks, upgradeHeightV2 - blocks.size(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
// Upgrade to v2 is here
createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_1);
uint64_t votingCompleteHeigntV3 = blocks.size() - 1;
uint64_t upgradeHeightV3 = currency.calculateUpgradeHeight(votingCompleteHeigntV3);
createBlocks(blocks, upgradeHeightV3 - blocks.size(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0);
// Upgrade to v3 is here
createBlocks(blocks, 1, BLOCK_V3, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_V3, BLOCK_MINOR_VERSION_1);
uint64_t votingCompleteHeigntV4 = blocks.size() - 1;
uint64_t upgradeHeightV4 = currency.calculateUpgradeHeight(votingCompleteHeigntV4);
createBlocks(blocks, upgradeHeightV4 - blocks.size(), BLOCK_V3, BLOCK_MINOR_VERSION_0);
// Upgrade to v4 is here
createBlocks(blocks, 1, BLOCK_V4, BLOCK_MINOR_VERSION_0);
UpgradeDetector upgradeDetectorV2(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetectorV2.init());
ASSERT_EQ(upgradeDetectorV2.votingCompleteHeight(), votingCompleteHeigntV2);
ASSERT_EQ(upgradeDetectorV2.upgradeHeight(), upgradeHeightV2);
UpgradeDetector upgradeDetectorV3(currency, blocks, BLOCK_V3, logger);
ASSERT_TRUE(upgradeDetectorV3.init());
ASSERT_EQ(upgradeDetectorV3.votingCompleteHeight(), votingCompleteHeigntV3);
ASSERT_EQ(upgradeDetectorV3.upgradeHeight(), upgradeHeightV3);
UpgradeDetector upgradeDetectorV4(currency, blocks, BLOCK_V4, logger);
ASSERT_TRUE(upgradeDetectorV4.init());
ASSERT_EQ(upgradeDetectorV4.votingCompleteHeight(), votingCompleteHeigntV4);
ASSERT_EQ(upgradeDetectorV4.upgradeHeight(), upgradeHeightV4);
}
TEST_F(UpgradeDetector_upgradeHeight_init, handlesEmptyBlockchain) {
const uint64_t upgradeHeight = 17;
CryptoNote::Currency currency = createCurrency(upgradeHeight);
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainBeforeUpgrade) {
const uint64_t upgradeHeight = 17;
CryptoNote::Currency currency = createCurrency(upgradeHeight);
BlockVector blocks;
createBlocks(blocks, upgradeHeight, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainAtUpgrade) {
const uint64_t upgradeHeight = 17;
CryptoNote::Currency currency = createCurrency(upgradeHeight);
BlockVector blocks;
createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainAfterUpgrade) {
const uint64_t upgradeHeight = 17;
CryptoNote::Currency currency = createCurrency(upgradeHeight);
BlockVector blocks;
createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0);
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_voting, handlesVotingCompleteStartingEmptyBlockchain) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1);
}
TEST_F(UpgradeDetector_voting, handlesVotingCompleteStartingNonEmptyBlockchain) {
CryptoNote::Currency currency = createCurrency();
assert(currency.minNumberVotingBlocks() >= 2);
const uint64_t portion = currency.minNumberVotingBlocks() - currency.minNumberVotingBlocks() / 2;
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks() - portion, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
ASSERT_TRUE(upgradeDetector.init());
createBlocks(blocks, upgradeDetector, portion, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1);
}
TEST_F(UpgradeDetector_voting, handlesVotingCancelling) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
uint64_t votingCompleteHeight = blocks.size() - 1;
uint64_t hadrforkHeight = currency.calculateUpgradeHeight(votingCompleteHeight);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight);
createBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight);
// Cancel voting
popBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight - 1);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight);
popBlocks(blocks, upgradeDetector, 1);
ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT);
}
TEST_F(UpgradeDetector_voting, handlesVotingAndUpgradeCancelling) {
CryptoNote::Currency currency = createCurrency();
BlockVector blocks;
UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger);
ASSERT_TRUE(upgradeDetector.init());
createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1);
uint64_t votingCompleteHeight = blocks.size() - 1;
uint64_t hadrforkHeight = currency.calculateUpgradeHeight(votingCompleteHeight);
ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight());
createBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0);
createBlocks(blocks, upgradeDetector, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0);
ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight());
// Cancel upgrade (pop block v2)
popBlocks(blocks, upgradeDetector, 1);
ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight());
// Pop blocks after voting
popBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight);
ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight());
// Cancel voting
popBlocks(blocks, upgradeDetector, 1);
ASSERT_EQ(UpgradeDetector::UNDEF_HEIGHT, upgradeDetector.votingCompleteHeight());
}
}