aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp5
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h2
-rw-r--r--src/blockchain_db/blockchain_db.h14
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp15
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h4
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp1
-rw-r--r--src/cryptonote_basic/hardfork.h15
-rw-r--r--src/cryptonote_core/blockchain.cpp57
-rw-r--r--src/cryptonote_core/blockchain.h13
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp32
-rw-r--r--src/cryptonote_core/cryptonote_core.h2
-rw-r--r--src/daemon/command_parser_executor.cpp6
-rw-r--r--src/daemon/command_server.cpp1
-rw-r--r--src/daemon/daemon.cpp3
-rw-r--r--src/daemon/main.cpp5
-rw-r--r--src/daemon/rpc_command_executor.cpp32
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/p2p/net_node.inl3
-rw-r--r--src/rpc/core_rpc_server.cpp80
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h31
-rw-r--r--src/rpc/core_rpc_server_error_codes.h1
-rw-r--r--src/rpc/message_data_structs.h1
-rw-r--r--src/serialization/json_object.cpp2
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/wallet2.cpp31
-rw-r--r--src/wallet/wallet2.h1
-rw-r--r--tests/README.md14
-rwxr-xr-xtests/functional_tests/blockchain.py103
-rwxr-xr-xtests/functional_tests/speed.py87
-rw-r--r--tests/functional_tests/test_framework/__init__.py0
-rw-r--r--tests/functional_tests/test_framework/daemon.py105
-rw-r--r--tests/functional_tests/test_framework/rpc.py49
-rw-r--r--tests/functional_tests/test_framework/wallet.py120
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/device.cpp131
-rw-r--r--tests/unit_tests/hardfork.cpp1
37 files changed, 926 insertions, 48 deletions
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index e1b76ec1e..f827ab7c3 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -1213,6 +1213,11 @@ std::vector<std::string> BlockchainBDB::get_filenames() const
return full_paths;
}
+bool BlockchainBDB::remove_data_file(const std::string& folder)
+{
+ return true;
+}
+
std::string BlockchainBDB::get_db_name() const
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index cecbba28f..c90d030a2 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -244,6 +244,8 @@ public:
virtual std::vector<std::string> get_filenames() const;
+ virtual bool remove_data_file(const std::string& folder);
+
virtual std::string get_db_name() const;
virtual bool lock();
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index a52782b3c..d91d3d5fc 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -656,6 +656,20 @@ public:
*/
virtual std::vector<std::string> get_filenames() const = 0;
+ /**
+ * @brief remove file(s) storing the database
+ *
+ * This function is for resetting the database (for core tests, functional tests, etc).
+ * The function reset() is not usable because it needs to open the database file first
+ * which can fail if the existing database file is in an incompatible format.
+ * As such, this function needs to be called before calling open().
+ *
+ * @param folder The path of the folder containing the database file(s) which must not end with slash '/'.
+ *
+ * @return true if the operation is succesfull
+ */
+ virtual bool remove_data_file(const std::string& folder) const = 0;
+
// return the name of the folder the db's file(s) should reside in
/**
* @brief gets the name of the folder the BlockchainDB's file(s) should be in
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 3aaade3b4..b7e0242dc 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1490,6 +1490,21 @@ std::vector<std::string> BlockchainLMDB::get_filenames() const
return filenames;
}
+bool BlockchainLMDB::remove_data_file(const std::string& folder) const
+{
+ const std::string filename = folder + "/data.mdb";
+ try
+ {
+ boost::filesystem::remove(filename);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to remove " << filename << ": " << e.what());
+ return false;
+ }
+ return true;
+}
+
std::string BlockchainLMDB::get_db_name() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index b7f6262ae..6210d3687 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -166,7 +166,7 @@ struct mdb_txn_safe
class BlockchainLMDB : public BlockchainDB
{
public:
- BlockchainLMDB(bool batch_transactions=false);
+ BlockchainLMDB(bool batch_transactions=true);
~BlockchainLMDB();
virtual void open(const std::string& filename, const int mdb_flags=0);
@@ -181,6 +181,8 @@ public:
virtual std::vector<std::string> get_filenames() const;
+ virtual bool remove_data_file(const std::string& folder) const;
+
virtual std::string get_db_name() const;
virtual bool lock();
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index c76641598..7291dbd68 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -33,6 +33,7 @@
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
+#include <unistd.h>
#include "misc_log_ex.h"
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h
index ee5ec0596..a63a66976 100644
--- a/src/cryptonote_basic/hardfork.h
+++ b/src/cryptonote_basic/hardfork.h
@@ -220,6 +220,14 @@ namespace cryptonote
*/
uint64_t get_window_size() const { return window_size; }
+ struct Params {
+ uint8_t version;
+ uint8_t threshold;
+ uint64_t height;
+ time_t time;
+ Params(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), threshold(threshold), height(height), time(time) {}
+ };
+
private:
uint8_t get_block_version(uint64_t height) const;
@@ -244,13 +252,6 @@ namespace cryptonote
uint8_t original_version;
uint64_t original_version_till_height;
- struct Params {
- uint8_t version;
- uint8_t threshold;
- uint64_t height;
- time_t time;
- Params(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), threshold(threshold), height(height), time(time) {}
- };
std::vector<Params> heights;
std::deque<uint8_t> versions; /* rolling window of the last N blocks' versions */
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index a7cd0947a..c729f5cc6 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -330,7 +330,7 @@ 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 network_type nettype, bool offline, const cryptonote::test_options *test_options)
+bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_tx_pool);
@@ -352,6 +352,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_nettype = test_options != NULL ? FAKECHAIN : nettype;
m_offline = offline;
+ m_fixed_difficulty = fixed_difficulty;
if (m_hardfork == nullptr)
{
if (m_nettype == FAKECHAIN || m_nettype == STAGENET)
@@ -795,6 +796,11 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
// less blocks than desired if there aren't enough.
difficulty_type Blockchain::get_difficulty_for_next_block()
{
+ if (m_fixed_difficulty)
+ {
+ return m_db->height() ? m_fixed_difficulty : 1;
+ }
+
LOG_PRINT_L3("Blockchain::" << __func__);
crypto::hash top_hash = get_tail_id();
@@ -1006,6 +1012,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
// an alternate chain.
difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
{
+ if (m_fixed_difficulty)
+ {
+ return m_db->height() ? m_fixed_difficulty : 1;
+ }
+
LOG_PRINT_L3("Blockchain::" << __func__);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> cumulative_difficulties;
@@ -4434,6 +4445,39 @@ HardFork::State Blockchain::get_hard_fork_state() const
return m_hardfork->get_state();
}
+const std::vector<HardFork::Params>& Blockchain::get_hard_fork_heights(network_type nettype)
+{
+ static const std::vector<HardFork::Params> mainnet_heights = []()
+ {
+ std::vector<HardFork::Params> heights;
+ for (const auto& i : mainnet_hard_forks)
+ heights.emplace_back(i.version, i.height, i.threshold, i.time);
+ return heights;
+ }();
+ static const std::vector<HardFork::Params> testnet_heights = []()
+ {
+ std::vector<HardFork::Params> heights;
+ for (const auto& i : testnet_hard_forks)
+ heights.emplace_back(i.version, i.height, i.threshold, i.time);
+ return heights;
+ }();
+ static const std::vector<HardFork::Params> stagenet_heights = []()
+ {
+ std::vector<HardFork::Params> heights;
+ for (const auto& i : stagenet_hard_forks)
+ heights.emplace_back(i.version, i.height, i.threshold, i.time);
+ return heights;
+ }();
+ static const std::vector<HardFork::Params> dummy;
+ switch (nettype)
+ {
+ case MAINNET: return mainnet_heights;
+ case TESTNET: return testnet_heights;
+ case STAGENET: return stagenet_heights;
+ default: return dummy;
+ }
+}
+
bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const
{
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
@@ -4449,9 +4493,9 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_ou
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);
}
-std::list<std::pair<Blockchain::block_extended_info,uint64_t>> Blockchain::get_alternative_chains() const
+std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
{
- std::list<std::pair<Blockchain::block_extended_info,uint64_t>> chains;
+ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
for (const auto &i: m_alternative_chains)
{
@@ -4467,15 +4511,16 @@ std::list<std::pair<Blockchain::block_extended_info,uint64_t>> Blockchain::get_a
}
if (!found)
{
- uint64_t length = 1;
+ std::vector<crypto::hash> chain;
auto h = i.second.bl.prev_id;
+ chain.push_back(top);
blocks_ext_by_hash::const_iterator prev;
while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end())
{
+ chain.push_back(h);
h = prev->second.bl.prev_id;
- ++length;
}
- chains.push_back(std::make_pair(i.second, length));
+ chains.push_back(std::make_pair(i.second, chain));
}
}
return chains;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 36d6b8609..d95c8ed15 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -114,10 +114,11 @@ namespace cryptonote
* @param nettype network type
* @param offline true if running offline, else false
* @param test_options test parameters
+ * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL);
+ bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0);
/**
* @brief Initialize the Blockchain state
@@ -755,6 +756,13 @@ namespace cryptonote
HardFork::State get_hard_fork_state() const;
/**
+ * @brief gets the hardfork heights of given network
+ *
+ * @return the HardFork object
+ */
+ static const std::vector<HardFork::Params>& get_hard_fork_heights(network_type nettype);
+
+ /**
* @brief gets the current hardfork version in use/voted for
*
* @return the version
@@ -939,7 +947,7 @@ namespace cryptonote
*
* @return a list of chains
*/
- std::list<std::pair<block_extended_info,uint64_t>> get_alternative_chains() const;
+ std::list<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
void add_txpool_tx(transaction &tx, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
@@ -1040,6 +1048,7 @@ namespace cryptonote
network_type m_nettype;
bool m_offline;
+ difficulty_type m_fixed_difficulty;
std::atomic<bool> m_cancel;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 910bf0c1f..18490c65e 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -76,6 +76,16 @@ namespace cryptonote
, "Run on stagenet. The wallet must be launched with --stagenet flag."
, false
};
+ const command_line::arg_descriptor<bool> arg_regtest_on = {
+ "regtest"
+ , "Run in a regression testing mode."
+ , false
+ };
+ const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty = {
+ "fixed-difficulty"
+ , "Fixed difficulty used for testing."
+ , 0
+ };
const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir = {
"data-dir"
, "Specify data directory"
@@ -251,6 +261,8 @@ namespace cryptonote
command_line::add_arg(desc, arg_testnet_on);
command_line::add_arg(desc, arg_stagenet_on);
+ command_line::add_arg(desc, arg_regtest_on);
+ command_line::add_arg(desc, arg_fixed_difficulty);
command_line::add_arg(desc, arg_dns_checkpoints);
command_line::add_arg(desc, arg_prep_blocks_threads);
command_line::add_arg(desc, arg_fast_block_sync);
@@ -373,7 +385,8 @@ namespace cryptonote
{
start_time = std::time(nullptr);
- if (test_options != NULL)
+ const bool regtest = command_line::get_arg(vm, arg_regtest_on);
+ if (test_options != NULL || regtest)
{
m_nettype = FAKECHAIN;
}
@@ -430,6 +443,16 @@ namespace cryptonote
blockchain_db_sync_mode sync_mode = db_defaultsync;
uint64_t blocks_per_sync = 1;
+ if (m_nettype == FAKECHAIN)
+ {
+ // reset the db by removing the database file before opening it
+ if (!db->remove_data_file(filename))
+ {
+ MERROR("Failed to remove data file in " << filename);
+ return false;
+ }
+ }
+
try
{
uint64_t db_flags = 0;
@@ -507,7 +530,12 @@ namespace cryptonote
m_blockchain_storage.set_user_options(blocks_threads,
blocks_per_sync, sync_mode, fast_sync);
- r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, test_options);
+ const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
+ const cryptonote::test_options regtest_test_options = {
+ regtest_hard_forks
+ };
+ const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
+ r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
r = m_mempool.init(max_txpool_size);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 03000383e..84e1bb918 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -60,6 +60,8 @@ namespace cryptonote
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir;
extern const command_line::arg_descriptor<bool, false> arg_testnet_on;
extern const command_line::arg_descriptor<bool, false> arg_stagenet_on;
+ extern const command_line::arg_descriptor<bool, false> arg_regtest_on;
+ extern const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty;
extern const command_line::arg_descriptor<bool> arg_offline;
/************************************************************************/
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index aa688294d..1638cf505 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -614,13 +614,13 @@ bool t_command_parser_executor::print_coinbase_tx_sum(const std::vector<std::str
bool t_command_parser_executor::alt_chain_info(const std::vector<std::string>& args)
{
- if(args.size())
+ if(args.size() > 1)
{
- std::cout << "No parameters allowed" << std::endl;
+ std::cout << "usage: alt_chain_info [block_hash]" << std::endl;
return false;
}
- return m_executor.alt_chain_info();
+ return m_executor.alt_chain_info(args.size() == 1 ? args[0] : "");
}
bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector<std::string>& args)
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 144603597..35504f2c9 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -255,6 +255,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"alt_chain_info"
, std::bind(&t_command_parser_executor::alt_chain_info, &m_parser, p::_1)
+ , "alt_chain_info [blockhash]"
, "Print the information about alternative chains."
);
m_command_lookup.set_handler(
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 48671f190..ea24e32eb 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -77,9 +77,10 @@ public:
const auto testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
const auto stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
+ const auto regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on);
const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc);
const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
- rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, main_rpc_port, "core"});
+ rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : regtest ? cryptonote::FAKECHAIN : cryptonote::MAINNET, main_rpc_port, "core"});
auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg))
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 88bb1fd0c..82ece62a9 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -163,9 +163,10 @@ int main(int argc, char const * argv[])
const bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
const bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- if (testnet && stagenet)
+ const bool regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on);
+ if (testnet + stagenet + regtest > 1)
{
- std::cerr << "Can't specify more than one of --tesnet and --stagenet" << ENDL;
+ std::cerr << "Can't specify more than one of --tesnet and --stagenet and --regtest" << ENDL;
return 1;
}
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 956c84a01..5d1a9a556 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1628,7 +1628,7 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
return true;
}
-bool t_rpc_command_executor::alt_chain_info()
+bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
@@ -1663,12 +1663,32 @@ bool t_rpc_command_executor::alt_chain_info()
}
}
- tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto &chain: res.chains)
+ if (tip.empty())
{
- uint64_t start_height = (chain.height - chain.length + 1);
- tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
- << " deep), diff " << chain.difficulty << ": " << chain.block_hash;
+ tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
+ for (const auto &chain: res.chains)
+ {
+ uint64_t start_height = (chain.height - chain.length + 1);
+ tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
+ << " deep), diff " << chain.difficulty << ": " << chain.block_hash;
+ }
+ }
+ else
+ {
+ const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; });
+ if (i != res.chains.end())
+ {
+ const auto &chain = *i;
+ tools::success_msg_writer() << "Found alternate chain with tip " << tip;
+ uint64_t start_height = (chain.height - chain.length + 1);
+ tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
+ << " deep), diff " << chain.difficulty << ":";
+ for (const std::string &block_id: chain.block_hashes)
+ tools::msg_writer() << " " << block_id;
+ tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block;
+ }
+ else
+ tools::fail_msg_writer() << "Block hash " << tip << " is not the tip of any known alternate chain";
}
return true;
}
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 46168c93b..9e6010c5b 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -143,7 +143,7 @@ public:
bool print_coinbase_tx_sum(uint64_t height, uint64_t count);
- bool alt_chain_info();
+ bool alt_chain_info(const std::string &tip);
bool print_blockchain_dynamic_stats(uint64_t nblocks);
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 85470f799..9390626a8 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -402,6 +402,9 @@ namespace nodetool
full_addrs.insert("162.210.173.150:38080");
full_addrs.insert("162.210.173.151:38080");
}
+ else if (nettype == cryptonote::FAKECHAIN)
+ {
+ }
else
{
full_addrs.insert("107.152.130.98:18080");
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 857370dbb..7db6b7f20 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -193,6 +193,7 @@ namespace cryptonote
res.mainnet = m_nettype == MAINNET;
res.testnet = m_nettype == TESTNET;
res.stagenet = m_nettype == STAGENET;
+ res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median();
@@ -1209,6 +1210,68 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp)
+ {
+ PERF_TIMER(on_generateblocks);
+
+ CHECK_CORE_READY();
+
+ res.status = CORE_RPC_STATUS_OK;
+
+ if(m_core.get_nettype() != FAKECHAIN)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_REGTEST_REQUIRED;
+ error_resp.message = "Regtest required when generating blocks";
+ return false;
+ }
+
+ COMMAND_RPC_GETBLOCKTEMPLATE::request template_req;
+ COMMAND_RPC_GETBLOCKTEMPLATE::response template_res;
+ COMMAND_RPC_SUBMITBLOCK::request submit_req;
+ COMMAND_RPC_SUBMITBLOCK::response submit_res;
+
+ template_req.reserve_size = 1;
+ template_req.wallet_address = req.wallet_address;
+ submit_req.push_back(boost::value_initialized<std::string>());
+ res.height = m_core.get_blockchain_storage().get_current_blockchain_height();
+
+ bool r;
+
+ for(size_t i = 0; i < req.amount_of_blocks; i++)
+ {
+ r = on_getblocktemplate(template_req, template_res, error_resp);
+ res.status = template_res.status;
+
+ if (!r) return false;
+
+ blobdata blockblob;
+ if(!string_tools::parse_hexstr_to_binbuff(template_res.blocktemplate_blob, blockblob))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_resp.message = "Wrong block blob";
+ return false;
+ }
+ block b = AUTO_VAL_INIT(b);
+ if(!parse_and_validate_block_from_blob(blockblob, b))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_resp.message = "Wrong block blob";
+ return false;
+ }
+ miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height);
+
+ submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b));
+ r = on_submitblock(submit_req, submit_res, error_resp);
+ res.status = submit_res.status;
+
+ if (!r) return false;
+
+ res.height = template_res.height;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
uint64_t core_rpc_server::get_block_reward(const block& blk)
{
uint64_t reward = 0;
@@ -1564,6 +1627,7 @@ namespace cryptonote
res.mainnet = m_nettype == MAINNET;
res.testnet = m_nettype == TESTNET;
res.stagenet = m_nettype == STAGENET;
+ res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median();
@@ -1774,10 +1838,22 @@ namespace cryptonote
PERF_TIMER(on_get_alternate_chains);
try
{
- std::list<std::pair<Blockchain::block_extended_info, uint64_t>> chains = m_core.get_blockchain_storage().get_alternative_chains();
+ std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
for (const auto &i: chains)
{
- res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second, i.first.cumulative_difficulty});
+ res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()});
+ res.chains.back().block_hashes.reserve(i.second.size());
+ for (const crypto::hash &block_id: i.second)
+ res.chains.back().block_hashes.push_back(epee::string_tools::pod_to_hex(block_id));
+ if (i.first.height < i.second.size())
+ {
+ res.status = "Error finding alternate chain attachment point";
+ return true;
+ }
+ cryptonote::block main_chain_parent_block;
+ try { main_chain_parent_block = m_core.get_blockchain_storage().get_db().get_block_from_height(i.first.height - i.second.size()); }
+ catch (const std::exception &e) { res.status = "Error finding alternate chain attachment point"; return true; }
+ res.chains.back().main_chain_parent_block = epee::string_tools::pod_to_hex(get_block_hash(main_chain_parent_block));
}
res.status = CORE_RPC_STATUS_OK;
}
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 324f219f8..5e62bc4a8 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -129,6 +129,7 @@ namespace cryptonote
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
+ MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted)
MAP_JON_RPC_WE("get_last_block_header", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("get_block_header_by_hash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH)
@@ -196,6 +197,7 @@ namespace cryptonote
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp);
+ bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp);
bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp);
bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 05690390d..6738171e2 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -955,6 +955,7 @@ namespace cryptonote
bool mainnet;
bool testnet;
bool stagenet;
+ std::string nettype;
std::string top_block_hash;
uint64_t cumulative_difficulty;
uint64_t block_size_limit;
@@ -984,6 +985,7 @@ namespace cryptonote
KV_SERIALIZE(mainnet)
KV_SERIALIZE(testnet)
KV_SERIALIZE(stagenet)
+ KV_SERIALIZE(nettype)
KV_SERIALIZE(top_block_hash)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(block_size_limit)
@@ -1151,6 +1153,31 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
+
+ struct COMMAND_RPC_GENERATEBLOCKS
+ {
+ struct request
+ {
+ uint64_t amount_of_blocks;
+ std::string wallet_address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_of_blocks)
+ KV_SERIALIZE(wallet_address)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t height;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
struct block_header_response
{
@@ -2090,12 +2117,16 @@ namespace cryptonote
uint64_t height;
uint64_t length;
uint64_t difficulty;
+ std::vector<std::string> block_hashes;
+ std::string main_chain_parent_block;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_hash)
KV_SERIALIZE(height)
KV_SERIALIZE(length)
KV_SERIALIZE(difficulty)
+ KV_SERIALIZE(block_hashes)
+ KV_SERIALIZE(main_chain_parent_block)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h
index 69caaa6a6..5a754749f 100644
--- a/src/rpc/core_rpc_server_error_codes.h
+++ b/src/rpc/core_rpc_server_error_codes.h
@@ -42,5 +42,6 @@
#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10
#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
#define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12
+#define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index 17ae9629f..fc1b2329d 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -181,6 +181,7 @@ namespace rpc
bool mainnet;
bool testnet;
bool stagenet;
+ std::string nettype;
crypto::hash top_block_hash;
uint64_t cumulative_difficulty;
uint64_t block_size_limit;
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index a7fb58ee4..c2467b863 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1175,6 +1175,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in
INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size);
INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size);
INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet);
+ INSERT_INTO_JSON_OBJECT(val, doc, nettype, info.nettype);
INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
@@ -1200,6 +1201,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size);
GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size);
GET_FROM_JSON_OBJECT(val, info.testnet, testnet);
+ GET_FROM_JSON_OBJECT(val, info.nettype, nettype);
GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
diff --git a/src/version.cpp.in b/src/version.cpp.in
index a03da7889..55ba51f50 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.12.2.0-master"
+#define DEF_MONERO_VERSION "0.12.3.0-master"
#define DEF_MONERO_RELEASE_NAME "Lithium Luna"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index d99371673..40a43b8e7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1057,6 +1057,16 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
tx_scan_info.error = false;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const
+{
+ tx_scan_info.received = boost::none;
+ if (already_seen)
+ return;
+ check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
+ if (tx_scan_info.received)
+ already_seen = true;
+}
+//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
{
crypto::secret_key scalar1;
@@ -1173,7 +1183,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// Don't try to extract tx public key if tx has no ouputs
size_t pk_index = 0;
std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
- std::unordered_set<crypto::public_key> public_keys_seen;
+ std::deque<bool> output_found(tx.vout.size(), false);
while (!tx.vout.empty())
{
// if tx.vout is not empty, we loop through all tx pubkeys
@@ -1194,13 +1204,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
error::wallet_internal_error, "tx_cache_data is out of sync");
}
- if (public_keys_seen.find(pub_key_field.pub_key) != public_keys_seen.end())
- {
- MWARNING("The same transaction pubkey is present more than once, ignoring extra instance");
- continue;
- }
- public_keys_seen.insert(pub_key_field.pub_key);
-
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
tools::threadpool& tpool = tools::threadpool::getInstance();
@@ -1264,7 +1267,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
- check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]);
+ check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
// this assumes that the miner tx pays a single address
@@ -1274,8 +1277,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true);
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
waiter.wait(&tpool);
// then scan all outputs from 0
@@ -1297,8 +1300,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true);
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
waiter.wait(&tpool);
@@ -1319,7 +1322,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]);
+ check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d33d8258b..4d22b6915 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1181,6 +1181,7 @@ namespace tools
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
+ void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const;
diff --git a/tests/README.md b/tests/README.md
index 48a6c41a7..0bf097254 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -50,6 +50,20 @@ To run the same tests on a release build, replace `debug` with `release`.
# Functional tests
[TODO]
+Functional tests are located under the `tests/functional` directory.
+
+First, run a regtest daemon in the offline mode and with a fixed difficulty:
+```
+monerod --regtest --offline --fixed-difficulty 1
+```
+Alternatively, you can run multiple daemons and let them connect with each other by using `--add-exclusive-node`. In this case, make sure that the same fixed difficulty is given to all the daemons.
+
+Next, restore a mainnet wallet with the following seed and restore height 0 (the file path doesn't matter):
+```
+velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted
+```
+
+Open the wallet file with `monero-wallet-rpc` with RPC port 18083. Finally, start tests by invoking ./blockchain.py or ./speed.py
# Fuzz tests
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
new file mode 100755
index 000000000..983658a7c
--- /dev/null
+++ b/tests/functional_tests/blockchain.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018 The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Test blockchain RPC calls
+
+Test the following RPCs:
+ - get_info
+ - generateblocks
+ - [TODO: many tests still need to be written]
+
+"""
+
+from test_framework.daemon import Daemon
+from test_framework.wallet import Wallet
+
+class BlockchainTest():
+ def run_test(self):
+ self._test_get_info()
+ self._test_hardfork_info()
+ self._test_generateblocks(5)
+
+ def _test_get_info(self):
+ print('Test get_info')
+
+ daemon = Daemon()
+ res = daemon.get_info()
+
+ # difficulty should be set to 1 for this test
+ assert 'difficulty' in res.keys()
+ assert res['difficulty'] == 1;
+
+ # nettype should not be TESTNET
+ assert 'testnet' in res.keys()
+ assert res['testnet'] == False;
+
+ # nettype should not be STAGENET
+ assert 'stagenet' in res.keys()
+ assert res['stagenet'] == False;
+
+ # nettype should be FAKECHAIN
+ assert 'nettype' in res.keys()
+ assert res['nettype'] == "fakechain";
+
+ # free_space should be > 0
+ assert 'free_space' in res.keys()
+ assert res['free_space'] > 0
+
+ # height should be greater or equal to 1
+ assert 'height' in res.keys()
+ assert res['height'] >= 1
+
+
+ def _test_hardfork_info(self):
+ print('Test hard_fork_info')
+
+ daemon = Daemon()
+ res = daemon.hard_fork_info()
+
+ # hard_fork version should be set at height 1
+ assert 'earliest_height' in res.keys()
+ assert res['earliest_height'] == 1;
+
+
+ def _test_generateblocks(self, blocks):
+ print("Test generating", blocks, 'blocks')
+
+ daemon = Daemon()
+ res = daemon.get_info()
+ height = res['height']
+ res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
+
+ assert res['height'] == height + blocks - 1
+
+
+if __name__ == '__main__':
+ BlockchainTest().run_test()
diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py
new file mode 100755
index 000000000..3d2af9a10
--- /dev/null
+++ b/tests/functional_tests/speed.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018 The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Test speed of various procedures
+
+Test the following RPCs:
+ - generateblocks
+ - transfer
+ - [TODO: many tests still need to be written]
+
+"""
+
+
+import time
+from time import sleep
+from decimal import Decimal
+
+from test_framework.daemon import Daemon
+from test_framework.wallet import Wallet
+
+
+class SpeedTest():
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ daemon = Daemon()
+ wallet = Wallet()
+
+ destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1,3)
+
+ self._test_speed_generateblocks(daemon=daemon, blocks=70)
+ for i in range(1, 10):
+ while wallet.get_balance()['unlocked_balance'] == 0:
+ print('Waiting for wallet to refresh...')
+ sleep(1)
+ self._test_speed_transfer_split(wallet=wallet)
+ self._test_speed_generateblocks(daemon=daemon, blocks=10)
+
+ def _test_speed_generateblocks(self, daemon, blocks):
+ print('Test speed of block generation')
+ start = time.time()
+
+ res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
+ # wallet seed: velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted
+
+ print('generating ', blocks, 'blocks took: ', time.time() - start, 'seconds')
+
+ def _test_speed_transfer_split(self, wallet):
+ print('Test speed of transfer')
+ start = time.time()
+
+ destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1)
+ res = wallet.transfer_split(destinations)
+
+ print('generating tx took: ', time.time() - start, 'seconds')
+
+
+if __name__ == '__main__':
+ SpeedTest().run_test()
diff --git a/tests/functional_tests/test_framework/__init__.py b/tests/functional_tests/test_framework/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/functional_tests/test_framework/__init__.py
diff --git a/tests/functional_tests/test_framework/daemon.py b/tests/functional_tests/test_framework/daemon.py
new file mode 100644
index 000000000..f3490b232
--- /dev/null
+++ b/tests/functional_tests/test_framework/daemon.py
@@ -0,0 +1,105 @@
+# Copyright (c) 2018 The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Daemon class to make rpc calls and store state."""
+
+from .rpc import JSONRPC
+
+class Daemon(object):
+
+ def __init__(self, protocol='http', host='127.0.0.1', port=18081, path='/json_rpc'):
+ self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path))
+
+ def getblocktemplate(self, address):
+ getblocktemplate = {
+ 'method': 'getblocktemplate',
+ 'params': {
+ 'wallet_address': address,
+ 'reserve_size' : 1
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(getblocktemplate)
+
+ def submitblock(self, block):
+ submitblock = {
+ 'method': 'submitblock',
+ 'params': [ block ],
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(submitblock)
+
+ def getblock(self, height=0):
+ getblock = {
+ 'method': 'getblock',
+ 'params': {
+ 'height': height
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(getblock)
+
+ def get_connections(self):
+ get_connections = {
+ 'method': 'get_connections',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(get_connections)
+
+ def get_info(self):
+ get_info = {
+ 'method': 'get_info',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(get_info)
+
+ def hard_fork_info(self):
+ hard_fork_info = {
+ 'method': 'hard_fork_info',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(hard_fork_info)
+
+ def generateblocks(self, address, blocks=1):
+ generateblocks = {
+ 'method': 'generateblocks',
+ 'params': {
+ 'amount_of_blocks' : blocks,
+ 'reserve_size' : 20,
+ 'wallet_address': address
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(generateblocks)
diff --git a/tests/functional_tests/test_framework/rpc.py b/tests/functional_tests/test_framework/rpc.py
new file mode 100644
index 000000000..b21df7b93
--- /dev/null
+++ b/tests/functional_tests/test_framework/rpc.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2018 The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import requests
+import json
+
+class JSONRPC(object):
+ def __init__(self, url):
+ self.url = url
+
+ def send_request(self, inputs):
+ res = requests.post(
+ self.url,
+ data=json.dumps(inputs),
+ headers={'content-type': 'application/json'})
+ res = res.json()
+
+ assert 'error' not in res, res
+
+ return res['result']
+
+
+
+
diff --git a/tests/functional_tests/test_framework/wallet.py b/tests/functional_tests/test_framework/wallet.py
new file mode 100644
index 000000000..357eab5b2
--- /dev/null
+++ b/tests/functional_tests/test_framework/wallet.py
@@ -0,0 +1,120 @@
+# Copyright (c) 2018 The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Daemon class to make rpc calls and store state."""
+
+from .rpc import JSONRPC
+
+class Wallet(object):
+
+ def __init__(self, protocol='http', host='127.0.0.1', port=18083, path='/json_rpc'):
+ self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path))
+
+ def make_uniform_destinations(self, address, transfer_amount, transfer_number_of_destinations=1):
+ destinations = []
+ for i in range(transfer_number_of_destinations):
+ destinations.append({"amount":transfer_amount,"address":address})
+ return destinations
+
+ def make_destinations(self, addresses, transfer_amounts):
+ destinations = []
+ for i in range(len(addresses)):
+ destinations.append({'amount':transfer_amounts[i],'address':addresses[i]})
+ return destinations
+
+ def transfer(self, destinations, ringsize=7, payment_id=''):
+ transfer = {
+ 'method': 'transfer',
+ 'params': {
+ 'destinations': destinations,
+ 'mixin' : ringsize - 1,
+ 'get_tx_key' : True
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ if(len(payment_id) > 0):
+ transfer['params'].update({'payment_id' : payment_id})
+ return self.rpc.send_request(transfer)
+
+ def transfer_split(self, destinations, ringsize=7, payment_id=''):
+ print(destinations)
+ transfer = {
+ "method": "transfer_split",
+ "params": {
+ "destinations": destinations,
+ "mixin" : ringsize - 1,
+ "get_tx_key" : True,
+ "new_algorithm" : True
+ },
+ "jsonrpc": "2.0",
+ "id": "0"
+ }
+ if(len(payment_id) > 0):
+ transfer['params'].update({'payment_id' : payment_id})
+ return self.rpc.send_request(transfer)
+
+ def create_wallet(self, index=''):
+ create_wallet = {
+ 'method': 'create_wallet',
+ 'params': {
+ 'filename': 'testWallet' + index,
+ 'password' : '',
+ 'language' : 'English'
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(create_wallet)
+
+ def get_balance(self):
+ get_balance = {
+ 'method': 'get_balance',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(get_balance)
+
+ def sweep_dust(self):
+ sweep_dust = {
+ 'method': 'sweep_dust',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(sweep_dust)
+
+ def sweep_all(self, address):
+ sweep_all = {
+ 'method': 'sweep_all',
+ 'params' : {
+ 'address' : ''
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request(sweep_all)
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 6d79ba74b..3105eccfa 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -41,6 +41,7 @@ set(unit_tests_sources
command_line.cpp
crypto.cpp
decompose_amount_into_digits.cpp
+ device.cpp
dns_resolver.cpp
epee_boosted_tcp_server.cpp
epee_levin_protocol_handler_async.cpp
diff --git a/tests/unit_tests/device.cpp b/tests/unit_tests/device.cpp
new file mode 100644
index 000000000..50ccec9fa
--- /dev/null
+++ b/tests/unit_tests/device.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "gtest/gtest.h"
+#include "ringct/rctOps.h"
+#include "device/device_default.hpp"
+
+TEST(device, name)
+{
+ hw::core::device_default dev;
+ ASSERT_TRUE(dev.set_name("test"));
+ ASSERT_EQ(dev.get_name(), "test");
+}
+
+/*
+TEST(device, locking)
+{
+ hw::core::device_default dev;
+ ASSERT_TRUE(dev.try_lock());
+ ASSERT_FALSE(dev.try_lock());
+ dev.unlock();
+ ASSERT_TRUE(dev.try_lock());
+ dev.unlock();
+ dev.lock();
+ ASSERT_FALSE(dev.try_lock());
+ dev.unlock();
+ ASSERT_TRUE(dev.try_lock());
+ dev.unlock();
+}
+*/
+
+TEST(device, open_close)
+{
+ hw::core::device_default dev;
+ crypto::secret_key key;
+ ASSERT_TRUE(dev.open_tx(key));
+ ASSERT_TRUE(dev.close_tx());
+}
+
+TEST(device, ops)
+{
+ hw::core::device_default dev;
+ rct::key resd, res;
+ crypto::key_derivation derd, der;
+ rct::key sk, pk;
+ crypto::secret_key sk0, sk1;
+ crypto::public_key pk0, pk1;
+ crypto::ec_scalar ressc0, ressc1;
+ crypto::key_image ki0, ki1;
+
+ rct::skpkGen(sk, pk);
+ rct::scalarmultBase((rct::key&)pk0, (rct::key&)sk0);
+ rct::scalarmultBase((rct::key&)pk1, (rct::key&)sk1);
+
+ dev.scalarmultKey(resd, pk, sk);
+ rct::scalarmultKey(res, pk, sk);
+ ASSERT_EQ(resd, res);
+
+ dev.scalarmultBase(resd, sk);
+ rct::scalarmultBase(res, sk);
+ ASSERT_EQ(resd, res);
+
+ dev.sc_secret_add((crypto::secret_key&)resd, sk0, sk1);
+ sc_add((unsigned char*)&res, (unsigned char*)&sk0, (unsigned char*)&sk1);
+ ASSERT_EQ(resd, res);
+
+ dev.generate_key_derivation(pk0, sk0, derd);
+ crypto::generate_key_derivation(pk0, sk0, der);
+ ASSERT_FALSE(memcmp(&derd, &der, sizeof(der)));
+
+ dev.derivation_to_scalar(der, 0, ressc0);
+ crypto::derivation_to_scalar(der, 0, ressc1);
+ ASSERT_FALSE(memcmp(&ressc0, &ressc1, sizeof(ressc1)));
+
+ dev.derive_secret_key(der, 0, rct::rct2sk(sk), sk0);
+ crypto::derive_secret_key(der, 0, rct::rct2sk(sk), sk1);
+ ASSERT_EQ(sk0, sk1);
+
+ dev.derive_public_key(der, 0, rct::rct2pk(pk), pk0);
+ crypto::derive_public_key(der, 0, rct::rct2pk(pk), pk1);
+ ASSERT_EQ(pk0, pk1);
+
+ dev.secret_key_to_public_key(rct::rct2sk(sk), pk0);
+ crypto::secret_key_to_public_key(rct::rct2sk(sk), pk1);
+ ASSERT_EQ(pk0, pk1);
+
+ dev.generate_key_image(pk0, sk0, ki0);
+ crypto::generate_key_image(pk0, sk0, ki1);
+ ASSERT_EQ(ki0, ki1);
+}
+
+TEST(device, ecdh)
+{
+ hw::core::device_default dev;
+ rct::ecdhTuple tuple, tuple2;
+ rct::key key = rct::skGen();
+ tuple.mask = rct::skGen();
+ tuple.amount = rct::skGen();
+ tuple.senderPk = rct::pkGen();
+ tuple2 = tuple;
+ dev.ecdhEncode(tuple, key);
+ dev.ecdhDecode(tuple, key);
+ ASSERT_EQ(tuple2.mask, tuple.mask);
+ ASSERT_EQ(tuple2.amount, tuple.amount);
+ ASSERT_EQ(tuple2.senderPk, tuple.senderPk);
+}
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index 110b6492a..1c875fd56 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -50,6 +50,7 @@ public:
virtual void safesyncmode(const bool onoff) {}
virtual void reset() {}
virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); }
+ virtual bool remove_data_file(const std::string& folder) const { return true; }
virtual std::string get_db_name() const { return std::string(); }
virtual bool lock() { return true; }
virtual void unlock() { }