mirror of
https://github.com/monero-project/monero.git
synced 2024-10-01 11:49:47 -04:00
Merge pull request #3854
149da42
db_lmdb: enable batch transactions by default (stoffu)34cb6b4
add --regtest and --fixed-difficulty for regression testing (vicsn)9e1403e
update get_info RPC and bump RPC version (vicsn)207b66e
first new functional tests (vicsn)
This commit is contained in:
commit
025187e6c9
@ -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__);
|
||||
|
@ -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();
|
||||
|
@ -655,6 +655,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
|
||||
|
@ -1469,6 +1469,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__);
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
@ -4405,6 +4416,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);
|
||||
|
@ -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
|
||||
@ -754,6 +755,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
|
||||
*
|
||||
@ -1040,6 +1048,7 @@ namespace cryptonote
|
||||
|
||||
network_type m_nettype;
|
||||
bool m_offline;
|
||||
difficulty_type m_fixed_difficulty;
|
||||
|
||||
std::atomic<bool> m_cancel;
|
||||
|
||||
|
@ -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 ? ®test_test_options : test_options, fixed_difficulty);
|
||||
|
||||
r = m_mempool.init(max_txpool_size);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
|
||||
|
@ -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;
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -194,6 +194,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();
|
||||
@ -1210,6 +1211,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;
|
||||
@ -1565,6 +1628,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();
|
||||
|
@ -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);
|
||||
|
@ -49,7 +49,7 @@ namespace cryptonote
|
||||
// advance which version they will stop working with
|
||||
// Don't go over 32767 for any of these
|
||||
#define CORE_RPC_VERSION_MAJOR 1
|
||||
#define CORE_RPC_VERSION_MINOR 20
|
||||
#define CORE_RPC_VERSION_MINOR 21
|
||||
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
103
tests/functional_tests/blockchain.py
Executable file
103
tests/functional_tests/blockchain.py
Executable file
@ -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()
|
87
tests/functional_tests/speed.py
Executable file
87
tests/functional_tests/speed.py
Executable file
@ -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()
|
0
tests/functional_tests/test_framework/__init__.py
Normal file
0
tests/functional_tests/test_framework/__init__.py
Normal file
105
tests/functional_tests/test_framework/daemon.py
Normal file
105
tests/functional_tests/test_framework/daemon.py
Normal file
@ -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)
|
49
tests/functional_tests/test_framework/rpc.py
Normal file
49
tests/functional_tests/test_framework/rpc.py
Normal file
@ -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']
|
||||
|
||||
|
||||
|
||||
|
120
tests/functional_tests/test_framework/wallet.py
Normal file
120
tests/functional_tests/test_framework/wallet.py
Normal file
@ -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)
|
@ -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() { }
|
||||
|
Loading…
Reference in New Issue
Block a user