2015-05-27 12:08:46 +00:00
|
|
|
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
|
2014-08-13 10:51:37 +00:00
|
|
|
//
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
2015-07-15 12:23:00 +00:00
|
|
|
#include <boost/filesystem/operations.hpp>
|
|
|
|
|
2014-08-13 10:51:37 +00:00
|
|
|
#include "cryptonote_core/account.h"
|
|
|
|
#include "cryptonote_core/cryptonote_format_utils.h"
|
|
|
|
#include "cryptonote_core/Currency.h"
|
|
|
|
#include "cryptonote_core/tx_pool.h"
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
#include <Logging/ConsoleLogger.h>
|
|
|
|
#include <Logging/LoggerGroup.h>
|
|
|
|
|
|
|
|
using namespace CryptoNote;
|
2014-08-13 10:51:37 +00:00
|
|
|
using namespace CryptoNote;
|
|
|
|
|
|
|
|
class TransactionValidator : public CryptoNote::ITransactionValidator {
|
2015-05-27 12:08:46 +00:00
|
|
|
virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) {
|
2014-08-13 10:51:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) {
|
2014-08-13 10:51:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) {
|
2014-08-13 10:51:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class FakeTimeProvider : public ITimeProvider {
|
|
|
|
public:
|
|
|
|
FakeTimeProvider(time_t currentTime = time(nullptr))
|
|
|
|
: timeNow(currentTime) {}
|
|
|
|
|
|
|
|
time_t timeNow;
|
|
|
|
virtual time_t now() { return timeNow; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class TestTransactionGenerator {
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TestTransactionGenerator(const CryptoNote::Currency& currency, size_t ringSize) :
|
2014-08-13 10:51:37 +00:00
|
|
|
m_currency(currency),
|
|
|
|
m_ringSize(ringSize),
|
|
|
|
m_miners(ringSize),
|
|
|
|
m_miner_txs(ringSize),
|
|
|
|
m_public_keys(ringSize),
|
|
|
|
m_public_key_ptrs(ringSize)
|
|
|
|
{
|
|
|
|
rv_acc.generate();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool createSources() {
|
|
|
|
|
|
|
|
size_t real_source_idx = m_ringSize / 2;
|
|
|
|
|
|
|
|
std::vector<tx_source_entry::output_entry> output_entries;
|
|
|
|
for (size_t i = 0; i < m_ringSize; ++i)
|
|
|
|
{
|
|
|
|
m_miners[i].generate();
|
|
|
|
|
|
|
|
if (!m_currency.constructMinerTx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionOutputToKey tx_out = boost::get<TransactionOutputToKey>(m_miner_txs[i].vout[0].target);
|
|
|
|
output_entries.push_back(std::make_pair(i, tx_out.key));
|
|
|
|
m_public_keys[i] = tx_out.key;
|
|
|
|
m_public_key_ptrs[i] = &m_public_keys[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
m_source_amount = m_miner_txs[0].vout[0].amount;
|
|
|
|
|
|
|
|
tx_source_entry source_entry;
|
|
|
|
source_entry.amount = m_source_amount;
|
|
|
|
source_entry.real_out_tx_key = get_tx_pub_key_from_extra(m_miner_txs[real_source_idx]);
|
|
|
|
source_entry.real_output_in_tx_index = 0;
|
|
|
|
source_entry.outputs.swap(output_entries);
|
|
|
|
source_entry.real_output = real_source_idx;
|
|
|
|
|
|
|
|
m_sources.push_back(source_entry);
|
|
|
|
|
|
|
|
m_realSenderKeys = m_miners[real_source_idx].get_keys();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void construct(uint64_t amount, uint64_t fee, size_t outputs, Transaction& tx) {
|
|
|
|
|
|
|
|
std::vector<tx_destination_entry> destinations;
|
|
|
|
uint64_t amountPerOut = (amount - fee) / outputs;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < outputs; ++i) {
|
|
|
|
destinations.push_back(tx_destination_entry(amountPerOut, rv_acc.get_keys().m_account_address));
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
construct_tx(m_realSenderKeys, m_sources, destinations, std::vector<uint8_t>(), tx, 0, m_logger);
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<account_base> m_miners;
|
|
|
|
std::vector<Transaction> m_miner_txs;
|
|
|
|
std::vector<tx_source_entry> m_sources;
|
|
|
|
std::vector<crypto::public_key> m_public_keys;
|
|
|
|
std::vector<const crypto::public_key*> m_public_key_ptrs;
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
Logging::LoggerGroup m_logger;
|
|
|
|
const CryptoNote::Currency& m_currency;
|
2014-08-13 10:51:37 +00:00
|
|
|
const size_t m_ringSize;
|
|
|
|
account_keys m_realSenderKeys;
|
|
|
|
uint64_t m_source_amount;
|
|
|
|
account_base rv_acc;
|
|
|
|
};
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
class tx_pool : public ::testing::Test {
|
|
|
|
public:
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
tx_pool() :
|
|
|
|
currency(CryptoNote::CurrencyBuilder(logger).currency()) {}
|
|
|
|
|
2015-07-15 12:23:00 +00:00
|
|
|
protected:
|
|
|
|
virtual void SetUp() override {
|
|
|
|
m_configDir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("test_data_%%%%%%%%%%%%");
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void TearDown() override {
|
|
|
|
boost::system::error_code ignoredErrorCode;
|
|
|
|
boost::filesystem::remove_all(m_configDir, ignoredErrorCode);
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
protected:
|
|
|
|
Logging::ConsoleLogger logger;
|
|
|
|
CryptoNote::Currency currency;
|
2015-07-15 12:23:00 +00:00
|
|
|
boost::filesystem::path m_configDir;
|
2015-05-27 12:08:46 +00:00
|
|
|
};
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
static const size_t textMaxCumulativeSize = std::numeric_limits<size_t>::max();
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
void GenerateTransaction(const CryptoNote::Currency& currency, Transaction& tx, uint64_t fee, size_t outputs) {
|
2014-08-13 10:51:37 +00:00
|
|
|
TestTransactionGenerator txGenerator(currency, 1);
|
|
|
|
txGenerator.createSources();
|
|
|
|
txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Validator, typename TimeProvider>
|
|
|
|
class TestPool : public tx_memory_pool {
|
|
|
|
public:
|
|
|
|
|
|
|
|
Validator validator;
|
|
|
|
TimeProvider timeProvider;
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TestPool(const CryptoNote::Currency& currency, Logging::ILogger& logger) :
|
|
|
|
tx_memory_pool(currency, validator, timeProvider, logger) {}
|
2014-08-13 10:51:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class TxTestBase {
|
|
|
|
public:
|
|
|
|
TxTestBase(size_t ringSize) :
|
2015-05-27 12:08:46 +00:00
|
|
|
m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()),
|
2014-08-13 10:51:37 +00:00
|
|
|
txGenerator(m_currency, ringSize),
|
2015-05-27 12:08:46 +00:00
|
|
|
pool(m_currency, validator, m_time, m_logger)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
|
|
|
txGenerator.createSources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void construct(uint64_t fee, size_t outputs, Transaction& tx) {
|
|
|
|
txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx);
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
Logging::ConsoleLogger m_logger;
|
|
|
|
CryptoNote::Currency m_currency;
|
2014-08-13 10:51:37 +00:00
|
|
|
CryptoNote::RealTimeProvider m_time;
|
|
|
|
TestTransactionGenerator txGenerator;
|
|
|
|
TransactionValidator validator;
|
|
|
|
tx_memory_pool pool;
|
|
|
|
};
|
|
|
|
|
|
|
|
void InitBlock(Block& bl, uint8_t majorVersion = BLOCK_MAJOR_VERSION_1) {
|
|
|
|
bl.majorVersion = majorVersion;
|
|
|
|
bl.minorVersion = 0;
|
|
|
|
bl.nonce = 0;
|
|
|
|
bl.timestamp = time(0);
|
|
|
|
bl.prevId = null_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, add_one_tx)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
|
|
|
TxTestBase test(1);
|
|
|
|
Transaction tx;
|
|
|
|
|
|
|
|
test.construct(test.m_currency.minimumFee(), 1, tx);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
|
|
|
|
ASSERT_TRUE(test.pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
};
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, take_tx)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
|
|
|
TxTestBase test(1);
|
|
|
|
Transaction tx;
|
|
|
|
|
|
|
|
test.construct(test.m_currency.minimumFee(), 1, tx);
|
|
|
|
|
|
|
|
auto txhash = get_transaction_hash(tx);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
|
|
|
|
ASSERT_TRUE(test.pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
|
|
|
|
Transaction txOut;
|
|
|
|
size_t blobSize;
|
|
|
|
uint64_t fee = 0;
|
|
|
|
|
|
|
|
ASSERT_TRUE(test.pool.take_tx(txhash, txOut, blobSize, fee));
|
|
|
|
ASSERT_EQ(fee, test.m_currency.minimumFee());
|
|
|
|
ASSERT_EQ(tx, txOut);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, double_spend_tx)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
|
|
|
TxTestBase test(1);
|
|
|
|
Transaction tx, tx_double;
|
|
|
|
|
|
|
|
test.construct(test.m_currency.minimumFee(), 1, tx);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
|
|
|
|
ASSERT_TRUE(test.pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
|
|
|
|
test.txGenerator.rv_acc.generate(); // generate new receiver address
|
|
|
|
test.construct(test.m_currency.minimumFee(), 1, tx_double);
|
|
|
|
|
|
|
|
ASSERT_FALSE(test.pool.add_tx(tx_double, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_verifivation_failed);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, fillblock_same_fee)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
2015-05-27 12:08:46 +00:00
|
|
|
TestPool<TransactionValidator, RealTimeProvider> pool(currency, logger);
|
2014-08-13 10:51:37 +00:00
|
|
|
uint64_t fee = currency.minimumFee();
|
|
|
|
|
|
|
|
std::unordered_map<crypto::hash, std::unique_ptr<Transaction>> transactions;
|
|
|
|
|
|
|
|
// generate transactions
|
|
|
|
for (int i = 1; i <= 50; ++i) {
|
|
|
|
TestTransactionGenerator txGenerator(currency, 1);
|
|
|
|
txGenerator.createSources();
|
|
|
|
|
|
|
|
std::unique_ptr<Transaction> txptr(new Transaction);
|
|
|
|
Transaction& tx = *txptr;
|
|
|
|
|
|
|
|
txGenerator.construct(txGenerator.m_source_amount, fee, i, tx);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
transactions[get_transaction_hash(tx)] = std::move(txptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Block bl;
|
|
|
|
|
|
|
|
InitBlock(bl);
|
|
|
|
|
|
|
|
size_t totalSize = 0;
|
|
|
|
uint64_t txFee = 0;
|
|
|
|
uint64_t median = 5000;
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool.fill_block_template(bl, median, textMaxCumulativeSize, 0, totalSize, txFee));
|
|
|
|
ASSERT_TRUE(totalSize*100 < median*125);
|
|
|
|
|
|
|
|
// now, check that the block is opimally filled
|
|
|
|
// if fee is fixed, transactions with smaller number of outputs should be included
|
|
|
|
|
|
|
|
size_t maxOuts = 0;
|
|
|
|
|
|
|
|
for (auto& th : bl.txHashes) {
|
|
|
|
auto iter = transactions.find(th);
|
|
|
|
ASSERT_TRUE(iter != transactions.end());
|
|
|
|
|
|
|
|
size_t txouts = iter->second->vout.size();
|
|
|
|
|
|
|
|
if (txouts > maxOuts)
|
|
|
|
maxOuts = txouts;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_TRUE(maxOuts <= bl.txHashes.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, fillblock_same_size)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
2015-05-27 12:08:46 +00:00
|
|
|
TestPool<TransactionValidator, RealTimeProvider> pool(currency, logger);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
const uint64_t fee = currency.minimumFee();
|
|
|
|
const size_t totalTransactions = 50;
|
|
|
|
|
|
|
|
std::unordered_map<crypto::hash, std::unique_ptr<Transaction>> transactions;
|
|
|
|
|
|
|
|
|
|
|
|
// generate transactions
|
|
|
|
for (int i = 0; i <= totalTransactions; ++i) {
|
|
|
|
|
|
|
|
TestTransactionGenerator txGenerator(currency, 1);
|
|
|
|
txGenerator.createSources();
|
|
|
|
|
|
|
|
std::unique_ptr<Transaction> txptr(new Transaction);
|
|
|
|
Transaction& tx = *txptr;
|
|
|
|
|
|
|
|
// interleave fee and fee*2
|
|
|
|
txGenerator.construct(txGenerator.m_source_amount, fee + (fee * (i&1)), 1, tx);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
transactions[get_transaction_hash(tx)] = std::move(txptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Block bl;
|
|
|
|
|
|
|
|
InitBlock(bl);
|
|
|
|
|
|
|
|
size_t totalSize = 0;
|
|
|
|
uint64_t txFee = 0;
|
|
|
|
uint64_t median = 5000;
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool.fill_block_template(bl, median, textMaxCumulativeSize, 0, totalSize, txFee));
|
|
|
|
ASSERT_TRUE(totalSize * 100 < median * 125);
|
|
|
|
|
|
|
|
// check that fill_block_template prefers transactions with double fee
|
|
|
|
|
|
|
|
size_t doubleFee = 0;
|
|
|
|
|
|
|
|
for (auto& th : bl.txHashes) {
|
|
|
|
|
|
|
|
auto iter = transactions.find(th);
|
|
|
|
ASSERT_TRUE(iter != transactions.end());
|
|
|
|
|
|
|
|
if (get_tx_fee(*iter->second) > fee)
|
|
|
|
++doubleFee;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_TRUE(doubleFee == std::min(bl.txHashes.size(), totalTransactions / 2));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, cleanup_stale_tx)
|
2014-08-13 10:51:37 +00:00
|
|
|
{
|
2015-05-27 12:08:46 +00:00
|
|
|
TestPool<TransactionValidator, FakeTimeProvider> pool(currency, logger);
|
2014-08-13 10:51:37 +00:00
|
|
|
const uint64_t fee = currency.minimumFee();
|
|
|
|
|
|
|
|
time_t startTime = pool.timeProvider.now();
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, fee, 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
pool.timeProvider.timeNow += 60 * 60 * 2; // add 2 hours
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, fee, 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, true)); // alternative chain
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
pool.timeProvider.timeNow += 60 * 60 * 2; // add 2 hours
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(8, pool.get_transactions_count());
|
|
|
|
|
|
|
|
pool.timeProvider.timeNow = startTime + currency.mempoolTxLiveTime() + 3*60*60;
|
|
|
|
pool.on_idle(); // 2 transactions should be removed
|
|
|
|
|
|
|
|
ASSERT_EQ(6, pool.get_transactions_count());
|
|
|
|
|
|
|
|
pool.timeProvider.timeNow = startTime + currency.mempoolTxFromAltBlockLiveTime() + (3*2+3) * 60 * 60;
|
|
|
|
pool.on_idle(); // all transactions from main chain and 2 transactions from altchain should be removed
|
|
|
|
|
|
|
|
ASSERT_EQ(3, pool.get_transactions_count());
|
|
|
|
}
|
2015-04-06 16:13:07 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TEST_F(tx_pool, add_tx_after_cleanup)
|
2015-04-06 16:13:07 +00:00
|
|
|
{
|
2015-05-27 12:08:46 +00:00
|
|
|
TestPool<TransactionValidator, FakeTimeProvider> pool(currency, logger);
|
2015-04-06 16:13:07 +00:00
|
|
|
const uint64_t fee = currency.minimumFee();
|
|
|
|
|
|
|
|
time_t startTime = pool.timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, fee, 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
2015-07-15 12:23:00 +00:00
|
|
|
uint64_t cleanupTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
pool.timeProvider.timeNow = cleanupTime;
|
|
|
|
pool.on_idle();
|
|
|
|
|
|
|
|
pool.timeProvider.timeNow = cleanupTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1;
|
2015-04-06 16:13:07 +00:00
|
|
|
pool.on_idle();
|
|
|
|
|
|
|
|
ASSERT_EQ(0, pool.get_transactions_count());
|
|
|
|
|
|
|
|
// add again
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
ASSERT_EQ(1, pool.get_transactions_count());
|
|
|
|
|
|
|
|
}
|
2015-07-15 12:23:00 +00:00
|
|
|
|
|
|
|
TEST_F(tx_pool, RecentlyDeletedTransactionCannotBeAddedToTxPoolAgain) {
|
|
|
|
TestPool<TransactionValidator, FakeTimeProvider> pool(currency, logger);
|
|
|
|
|
|
|
|
uint64_t startTime = pool.timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
pool.timeProvider.timeNow = deleteTime;
|
|
|
|
pool.on_idle();
|
|
|
|
ASSERT_EQ(0, pool.get_transactions_count());
|
|
|
|
|
|
|
|
// Try to add tx again
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_FALSE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_FALSE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, pool.get_transactions_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(tx_pool, RecentlyDeletedTransactionCanBeAddedAgainAfterSomeTime) {
|
|
|
|
TestPool<TransactionValidator, FakeTimeProvider> pool(currency, logger);
|
|
|
|
|
|
|
|
uint64_t startTime = pool.timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
pool.timeProvider.timeNow = deleteTime;
|
|
|
|
pool.on_idle();
|
|
|
|
ASSERT_EQ(0, pool.get_transactions_count());
|
|
|
|
|
|
|
|
uint64_t forgetDeletedTxTime = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1;
|
|
|
|
pool.timeProvider.timeNow = forgetDeletedTxTime;
|
|
|
|
pool.on_idle();
|
|
|
|
|
|
|
|
// Try to add tx again
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_TRUE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(1, pool.get_transactions_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(tx_pool, RecentlyDeletedTransactionCanBeAddedToTxPoolIfItIsReceivedInBlock) {
|
|
|
|
TestPool<TransactionValidator, FakeTimeProvider> pool(currency, logger);
|
|
|
|
|
|
|
|
uint64_t startTime = pool.timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
pool.timeProvider.timeNow = deleteTime;
|
|
|
|
pool.on_idle();
|
|
|
|
ASSERT_EQ(0, pool.get_transactions_count());
|
|
|
|
|
|
|
|
// Try to add tx again
|
|
|
|
ASSERT_TRUE(pool.add_tx(tx, tvc, true));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_TRUE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(1, pool.get_transactions_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(tx_pool, OldTransactionIsDeletedDuringTxPoolInitialization) {
|
|
|
|
TransactionValidator validator;
|
|
|
|
FakeTimeProvider timeProvider;
|
|
|
|
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
|
|
|
|
uint64_t startTime = timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool->deinit());
|
|
|
|
pool.reset();
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
timeProvider.timeNow = deleteTime;
|
|
|
|
|
|
|
|
pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
ASSERT_EQ(0, pool->get_transactions_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(tx_pool, TransactionThatWasDeletedLongAgoIsForgottenDuringTxPoolInitialization) {
|
|
|
|
TransactionValidator validator;
|
|
|
|
FakeTimeProvider timeProvider;
|
|
|
|
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
|
|
|
|
uint64_t startTime = timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
timeProvider.timeNow = deleteTime;
|
|
|
|
pool->on_idle();
|
|
|
|
ASSERT_EQ(0, pool->get_transactions_count());
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool->deinit());
|
|
|
|
pool.reset();
|
|
|
|
|
|
|
|
uint64_t forgetDeletedTxTime = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1;
|
|
|
|
timeProvider.timeNow = forgetDeletedTxTime;
|
|
|
|
|
|
|
|
pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
|
|
|
|
// Try to add tx again
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_TRUE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(1, pool->get_transactions_count());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(tx_pool, RecentlyDeletedTxInfoIsSerializedAndDeserialized) {
|
|
|
|
TransactionValidator validator;
|
|
|
|
FakeTimeProvider timeProvider;
|
|
|
|
std::unique_ptr<tx_memory_pool> pool(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
|
|
|
|
uint64_t startTime = timeProvider.now();
|
|
|
|
|
|
|
|
Transaction tx;
|
|
|
|
GenerateTransaction(currency, tx, currency.minimumFee(), 1);
|
|
|
|
|
|
|
|
tx_verification_context tvc = boost::value_initialized<tx_verification_context>();
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
|
|
|
|
uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1;
|
|
|
|
timeProvider.timeNow = deleteTime;
|
|
|
|
pool->on_idle();
|
|
|
|
ASSERT_EQ(0, pool->get_transactions_count());
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool->deinit());
|
|
|
|
|
|
|
|
pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger));
|
|
|
|
ASSERT_TRUE(pool->init(m_configDir.string()));
|
|
|
|
|
|
|
|
uint64_t timeBeforeCleanupDeletedTx = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime();
|
|
|
|
timeProvider.timeNow = timeBeforeCleanupDeletedTx;
|
|
|
|
pool->on_idle();
|
|
|
|
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_FALSE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_FALSE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, pool->get_transactions_count());
|
|
|
|
|
|
|
|
timeProvider.timeNow = timeBeforeCleanupDeletedTx + 61;
|
|
|
|
pool->on_idle();
|
|
|
|
|
|
|
|
// Try to add tx again
|
|
|
|
ASSERT_TRUE(pool->add_tx(tx, tvc, false));
|
|
|
|
ASSERT_TRUE(tvc.m_added_to_pool);
|
|
|
|
ASSERT_TRUE(tvc.m_should_be_relayed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_failed);
|
|
|
|
ASSERT_FALSE(tvc.m_verifivation_impossible);
|
|
|
|
|
|
|
|
ASSERT_EQ(1, pool->get_transactions_count());
|
|
|
|
}
|