From a333c42cdeab4675c22df25255313752d81a2d4e Mon Sep 17 00:00:00 2001
From: moneromooo-monero <moneromooo-monero@users.noreply.github.com>
Date: Mon, 8 Feb 2016 18:47:56 +0000
Subject: [PATCH] core_tests: add tests for hard fork behaviors (MRL-0004)

We also replace the --fakechain option with an optional structure
containing details about configuration for the core/blockchain,
for test purposes. This seems more future friendly.
---
 src/common/command_line.cpp             |  5 -----
 src/common/command_line.h               |  1 -
 src/cryptonote_core/blockchain.cpp      | 20 +++++++++++++++-----
 src/cryptonote_core/blockchain.h        |  5 +++--
 src/cryptonote_core/cryptonote_core.cpp |  7 +++----
 src/cryptonote_core/cryptonote_core.h   |  6 +++++-
 tests/core_tests/CMakeLists.txt         |  6 ++++--
 tests/core_tests/chaingen.h             | 15 ++++++++++-----
 tests/core_tests/chaingen_main.cpp      |  7 +++++++
 tests/core_tests/chaingen_tests_list.h  |  1 +
 10 files changed, 48 insertions(+), 25 deletions(-)

diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index b643f5135..98ff73bc5 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -91,9 +91,4 @@ namespace command_line
   , "Show time-stats when processing blocks/txs and disk synchronization."
   , 0
   };
-  const command_line::arg_descriptor<bool> arg_fakechain  = {
-    "fakechain"
-  , "Use a fake chain for testing purposes."
-  , false
-  };
 }
diff --git a/src/common/command_line.h b/src/common/command_line.h
index 2fa213a21..75dc4b9fd 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -215,5 +215,4 @@ namespace command_line
   extern const arg_descriptor<uint64_t> arg_prep_blocks_threads;
   extern const arg_descriptor<uint64_t> arg_db_auto_remove_logs;
   extern const arg_descriptor<uint64_t> arg_show_time_stats;
-  extern const arg_descriptor<bool> arg_fakechain;
 }
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 70953f3e7..7dd0aca42 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -50,6 +50,7 @@
 #include "warnings.h"
 #include "crypto/hash.h"
 #include "cryptonote_core/checkpoints_create.h"
+#include "cryptonote_core/cryptonote_core.h"
 #if defined(PER_BLOCK_CHECKPOINT)
 #include "blocks/blocks.h"
 #endif
@@ -252,11 +253,13 @@ uint64_t Blockchain::get_current_blockchain_height() const
 //------------------------------------------------------------------
 //FIXME: possibly move this into the constructor, to avoid accidentally
 //       dereferencing a null BlockchainDB pointer
-bool Blockchain::init(BlockchainDB* db, const bool testnet, const bool fakechain)
+bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options)
 {
   LOG_PRINT_L3("Blockchain::" << __func__);
   CRITICAL_REGION_LOCAL(m_blockchain_lock);
 
+  bool fakechain = test_options != NULL;
+
   if (db == nullptr)
   {
     LOG_ERROR("Attempted to init Blockchain with null DB");
@@ -273,12 +276,19 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const bool fakechain
   m_testnet = testnet;
   if (m_hardfork == nullptr)
   {
-    if (m_testnet)
+    if (fakechain)
+      m_hardfork = new HardFork(*db, 1, 0);
+    else if (m_testnet)
       m_hardfork = new HardFork(*db, 1, testnet_hard_fork_version_1_till);
     else
       m_hardfork = new HardFork(*db, 1, mainnet_hard_fork_version_1_till);
   }
-  if (m_testnet)
+  if (fakechain)
+  {
+    for (size_t n = 0; test_options->hard_forks[n].first; ++n)
+      m_hardfork->add_fork(test_options->hard_forks[n].first, test_options->hard_forks[n].second, 0, n + 1);
+  }
+  else if (m_testnet)
   {
     for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n)
       m_hardfork->add_fork(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, testnet_hard_forks[n].time);
@@ -348,11 +358,11 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const bool fakechain
   return true;
 }
 //------------------------------------------------------------------
-bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet, const bool fakechain)
+bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet)
 {
   if (hf != nullptr)
     m_hardfork = hf;
-  bool res = init(db, testnet, fakechain);
+  bool res = init(db, testnet, NULL);
   if (hf == nullptr)
     hf = m_hardfork;
   return res;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 5299fed99..ab2c8f9e8 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -58,6 +58,7 @@
 namespace cryptonote
 {
   class tx_memory_pool;
+  struct test_options;
 
   enum blockchain_db_sync_mode
   {
@@ -91,8 +92,8 @@ namespace cryptonote
 
     Blockchain(tx_memory_pool& tx_pool);
 
-    bool init(BlockchainDB* db, const bool testnet = false, const bool fakechain = false);
-    bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false, const bool fakechain = false);
+    bool init(BlockchainDB* db, const bool testnet = false, const cryptonote::test_options *test_options = NULL);
+    bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false);
     bool deinit();
 
     void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 2479931be..2c485d791 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -145,13 +145,11 @@ namespace cryptonote
     command_line::add_arg(desc, command_line::arg_db_sync_mode);
     command_line::add_arg(desc, command_line::arg_show_time_stats);
     command_line::add_arg(desc, command_line::arg_db_auto_remove_logs);
-    command_line::add_arg(desc, command_line::arg_fakechain);
   }
   //-----------------------------------------------------------------------------------------------
   bool core::handle_command_line(const boost::program_options::variables_map& vm)
   {
     m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
-    m_fakechain = command_line::get_arg(vm, command_line::arg_fakechain);
 
     auto data_dir_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
     m_config_folder = command_line::get_arg(vm, data_dir_arg);
@@ -251,8 +249,9 @@ namespace cryptonote
     return true;
   }
   //-----------------------------------------------------------------------------------------------
-  bool core::init(const boost::program_options::variables_map& vm)
+  bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options)
   {
+    m_fakechain = test_options != NULL;
     bool r = handle_command_line(vm);
 
     r = m_mempool.init(m_fakechain ? std::string() : m_config_folder);
@@ -398,7 +397,7 @@ namespace cryptonote
     m_blockchain_storage.set_user_options(blocks_threads,
         blocks_per_sync, sync_mode, fast_sync);
 
-    r = m_blockchain_storage.init(db, m_testnet, m_fakechain);
+    r = m_blockchain_storage.init(db, m_testnet, test_options);
 
     // now that we have a valid m_blockchain_storage, we can clean out any
     // transactions in the pool that do not conform to the current fork
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 90fdfefca..3c6f56749 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -56,6 +56,10 @@ DISABLE_VS_WARNINGS(4355)
 
 namespace cryptonote
 {
+   struct test_options {
+     const std::pair<uint8_t, uint64_t> *hard_forks;
+   };
+
   /************************************************************************/
   /*                                                                      */
   /************************************************************************/
@@ -81,7 +85,7 @@ namespace cryptonote
      miner& get_miner(){return m_miner;}
      const miner& get_miner()const{return m_miner;}
      static void init_options(boost::program_options::options_description& desc);
-     bool init(const boost::program_options::variables_map& vm);
+     bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL);
      bool set_genesis_block(const block& b);
      bool deinit();
      static void set_fast_exit();
diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt
index 5f34bdc10..c34039c17 100644
--- a/tests/core_tests/CMakeLists.txt
+++ b/tests/core_tests/CMakeLists.txt
@@ -38,7 +38,8 @@ set(core_tests_sources
   integer_overflow.cpp
   ring_signature_1.cpp
   transaction_tests.cpp
-  tx_validation.cpp)
+  tx_validation.cpp
+  v2_tests.cpp)
 
 set(core_tests_headers
   block_reward.h
@@ -52,7 +53,8 @@ set(core_tests_headers
   integer_overflow.h
   ring_signature_1.h
   transaction_tests.h
-  tx_validation.h)
+  tx_validation.h
+  v2_tests.h)
 
 add_executable(coretests
   ${core_tests_sources}
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index a5e93390d..f2bbb7346 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -471,6 +471,14 @@ inline bool replay_events_through_core(cryptonote::core& cr, const std::vector<t
 }
 //--------------------------------------------------------------------------
 template<class t_test_class>
+struct get_test_options {
+  const std::pair<uint8_t, uint64_t> hard_forks[1] = {std::make_pair(1, 0)};
+  const cryptonote::test_options test_options = {
+    hard_forks
+  };
+};
+//--------------------------------------------------------------------------
+template<class t_test_class>
 inline bool do_replay_events(std::vector<test_event_entry>& events)
 {
   boost::program_options::options_description desc("Allowed options");
@@ -485,15 +493,12 @@ inline bool do_replay_events(std::vector<test_event_entry>& events)
   if (!r)
     return false;
 
-  // hardcode a --fakechain option for tests
-  static const char * const fakechain[] = {"", "--fakechain"};
-  boost::program_options::store(boost::program_options::parse_command_line(2, fakechain, desc), vm);
-
   cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
   cryptonote::core c(&pr);
   // FIXME: make sure that vm has arg_testnet_on set to true or false if
   // this test needs for it to be so.
-  if (!c.init(vm))
+  const get_test_options<t_test_class> gto;
+  if (!c.init(vm, &gto.test_options))
   {
     std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl;
     return false;
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index 99ae496e3..40bce1e4e 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -168,6 +168,13 @@ int main(int argc, char* argv[])
 
     GENERATE_AND_PLAY(gen_block_reward);
 
+    GENERATE_AND_PLAY(gen_v2_tx_mixable_0_mixin);
+    GENERATE_AND_PLAY(gen_v2_tx_mixable_low_mixin);
+    GENERATE_AND_PLAY(gen_v2_tx_unmixable_only);
+    GENERATE_AND_PLAY(gen_v2_tx_unmixable_one);
+    GENERATE_AND_PLAY(gen_v2_tx_unmixable_two);
+    GENERATE_AND_PLAY(gen_v2_tx_dust);
+
     std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta);
     std::cout << "\nREPORT:\n";
     std::cout << "  Test run: " << tests_count << '\n';
diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h
index e18e00f74..4da87a973 100644
--- a/tests/core_tests/chaingen_tests_list.h
+++ b/tests/core_tests/chaingen_tests_list.h
@@ -39,6 +39,7 @@
 #include "integer_overflow.h"
 #include "ring_signature_1.h"
 #include "tx_validation.h"
+#include "v2_tests.h"
 /************************************************************************/
 /*                                                                      */
 /************************************************************************/