aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core_proxy/core_proxy.cpp18
-rw-r--r--tests/core_proxy/core_proxy.h10
-rw-r--r--tests/core_tests/chaingen.cpp4
-rw-r--r--tests/core_tests/chaingen.h18
-rw-r--r--tests/core_tests/chaingen_main.cpp1
-rw-r--r--tests/core_tests/rct.cpp31
-rw-r--r--tests/core_tests/rct.h14
-rw-r--r--tests/core_tests/tx_validation.cpp4
-rw-r--r--tests/functional_tests/CMakeLists.txt4
-rwxr-xr-xtests/functional_tests/address_book.py304
-rwxr-xr-xtests/functional_tests/blockchain.py6
-rw-r--r--tests/functional_tests/check_missing_rpc_methods.py50
-rwxr-xr-xtests/functional_tests/cold_signing.py8
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py9
-rwxr-xr-xtests/functional_tests/get_output_distribution.py3
-rwxr-xr-xtests/functional_tests/mining.py59
-rwxr-xr-xtests/functional_tests/multisig.py73
-rwxr-xr-xtests/functional_tests/proofs.py38
-rwxr-xr-xtests/functional_tests/speed.py22
-rw-r--r--tests/functional_tests/transactions_flow_test.cpp2
-rwxr-xr-xtests/functional_tests/transfer.py178
-rwxr-xr-xtests/functional_tests/txpool.py87
-rwxr-xr-xtests/functional_tests/uri.py234
-rwxr-xr-xtests/functional_tests/validate_address.py3
-rwxr-xr-xtests/functional_tests/wallet.py (renamed from tests/functional_tests/wallet_address.py)199
-rw-r--r--tests/performance_tests/rct_mlsag.h2
-rw-r--r--tests/unit_tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/blockchain_db.cpp9
-rw-r--r--tests/unit_tests/levin.cpp97
-rw-r--r--tests/unit_tests/logging.cpp17
-rw-r--r--tests/unit_tests/multiexp.cpp8
-rw-r--r--tests/unit_tests/net.cpp9
-rw-r--r--tests/unit_tests/node_server.cpp (renamed from tests/unit_tests/ban.cpp)69
-rw-r--r--tests/unit_tests/ringdb.cpp16
-rw-r--r--tests/unit_tests/test_protocol_pack.cpp2
35 files changed, 1490 insertions, 120 deletions
diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp
index 388808269..db5b7797a 100644
--- a/tests/core_proxy/core_proxy.cpp
+++ b/tests/core_proxy/core_proxy.cpp
@@ -160,7 +160,7 @@ string tx2str(const cryptonote::transaction& tx, const cryptonote::hash256& tx_h
return ss.str();
}*/
-bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) {
+bool tests::proxy_core::handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) {
if (!keeped_by_block)
return true;
@@ -168,7 +168,13 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob,
crypto::hash tx_prefix_hash = null_hash;
transaction tx;
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) {
+ if (tx_blob.prunable_hash != crypto::null_hash)
+ {
+ cerr << "WRONG TRANSACTION, pruned blob rejected" << endl;
+ return false;
+ }
+
+ if (!parse_and_validate_tx_from_blob(tx_blob.blob, tx, tx_hash, tx_prefix_hash)) {
cerr << "WRONG TRANSACTION BLOB, Failed to parse, rejected" << endl;
return false;
}
@@ -176,7 +182,7 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob,
cout << "TX " << endl << endl;
cout << tx_hash << endl;
cout << tx_prefix_hash << endl;
- cout << tx_blob.size() << endl;
+ cout << tx_blob.blob.size() << endl;
//cout << string_tools::buff_to_hex_nodelimer(tx_blob) << endl << endl;
cout << obj_to_json_str(tx) << endl;
cout << endl << "ENDTX" << endl;
@@ -184,7 +190,7 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob,
return true;
}
-bool tests::proxy_core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+bool tests::proxy_core::handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
tvc.resize(tx_blobs.size());
size_t i = 0;
@@ -209,7 +215,7 @@ bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_
crypto::hash lh;
cout << "BLOCK" << endl << endl;
cout << (h = get_block_hash(b)) << endl;
- cout << (lh = get_block_longhash(b, 0)) << endl;
+ cout << (lh = get_block_longhash(NULL, b, 0, 0)) << endl;
cout << get_transaction_hash(b.miner_tx) << endl;
cout << ::get_object_blobsize(b.miner_tx) << endl;
//cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl;
@@ -236,7 +242,7 @@ void tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i
bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) {
generate_genesis_block(m_genesis, config::GENESIS_TX, config::GENESIS_NONCE);
crypto::hash h = get_block_hash(m_genesis);
- add_block(h, get_block_longhash(m_genesis, 0), m_genesis, block_to_blob(m_genesis));
+ add_block(h, get_block_longhash(NULL, m_genesis, 0, 0), m_genesis, block_to_blob(m_genesis));
return true;
}
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h
index 6c8b3ccb6..0e41a2be0 100644
--- a/tests/core_proxy/core_proxy.h
+++ b/tests/core_proxy/core_proxy.h
@@ -75,13 +75,13 @@ namespace tests
bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;}
bool have_block(const crypto::hash& id);
void get_blockchain_top(uint64_t& height, crypto::hash& top_id);
- bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
- bool handle_incoming_txs(const std::vector<cryptonote::blobdata>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true);
void pause_mine(){}
void resume_mine(){}
bool on_idle(){return true;}
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;}
cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); }
bool get_test_drop_download() {return true;}
@@ -103,7 +103,9 @@ namespace tests
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; }
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
bool fluffy_blocks_enabled() const { return false; }
- uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; }
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { return 0; }
+ bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
+ bool is_within_compiled_block_hash_area(uint64_t height) const { return false; }
bool pad_transactions() const { return false; }
uint32_t get_blockchain_pruning_seed() const { return 0; }
bool prune_blockchain(uint32_t pruning_seed) const { return true; }
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index 8dde1f475..3f2984288 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -186,7 +186,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
// Nonce search...
blk.nonce = 0;
- while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(hf_ver), height))
+ while (!miner::find_nonce_for_given_block(NULL, blk, get_test_difficulty(hf_ver), height))
blk.timestamp++;
add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1);
@@ -797,7 +797,7 @@ void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
{
blk.nonce = 0;
- while (!miner::find_nonce_for_given_block(blk, diffic, height))
+ while (!miner::find_nonce_for_given_block(NULL, blk, diffic, height))
blk.timestamp++;
}
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index c0d31bb8a..b78640dc9 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -544,7 +544,7 @@ public:
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
size_t pool_size = m_c.get_pool_transactions_count();
- m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_txs_keeped_by_block, false, false);
+ m_c.handle_incoming_tx({t_serializable_object_to_blob(tx), crypto::null_hash}, tvc, m_txs_keeped_by_block, false, false);
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
bool r = m_validator.check_tx_verification_context(tvc, tx_added, m_ev_index, tx);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
@@ -555,12 +555,12 @@ public:
{
log_event("cryptonote::transaction");
- std::vector<cryptonote::blobdata> tx_blobs;
+ std::vector<cryptonote::tx_blob_entry> tx_blobs;
std::vector<cryptonote::tx_verification_context> tvcs;
cryptonote::tx_verification_context tvc0 = AUTO_VAL_INIT(tvc0);
for (const auto &tx: txs)
{
- tx_blobs.push_back(t_serializable_object_to_blob(tx));
+ tx_blobs.push_back({t_serializable_object_to_blob(tx)});
tvcs.push_back(tvc0);
}
size_t pool_size = m_c.get_pool_transactions_count();
@@ -578,7 +578,11 @@ public:
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
cryptonote::blobdata bd = t_serializable_object_to_blob(b);
std::vector<cryptonote::block> pblocks;
- if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, {bd, {}}), pblocks))
+ cryptonote::block_complete_entry bce;
+ bce.pruned = false;
+ bce.block = bd;
+ bce.txs = {};
+ if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, bce), pblocks))
{
m_c.handle_incoming_block(bd, &b, bvc);
m_c.cleanup_handle_incoming_blocks();
@@ -608,7 +612,11 @@ public:
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
std::vector<cryptonote::block> pblocks;
- if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, {sr_block.data, {}}), pblocks))
+ cryptonote::block_complete_entry bce;
+ bce.pruned = false;
+ bce.block = sr_block.data;
+ bce.txs = {};
+ if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, bce), pblocks))
{
m_c.handle_incoming_block(sr_block.data, NULL, bvc);
m_c.cleanup_handle_incoming_blocks();
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index 4ee71466e..8406f416f 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -208,6 +208,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee);
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra);
GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra);
+ GENERATE_AND_PLAY(gen_rct_tx_uses_output_too_early);
GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2);
GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2_many_inputs);
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 248354177..dc77e408f 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -40,8 +40,8 @@ using namespace cryptonote;
//----------------------------------------------------------------------------------------------------------------------
// Tests
-bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
- const int *out_idx, int mixin, uint64_t amount_paid, bool valid,
+bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry>& events,
+ const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
const std::function<void(transaction &tx)> &post_tx) const
{
@@ -151,13 +151,13 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
// rewind
{
- for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
+ for (size_t i = 0; i < second_rewind; ++i)
{
cryptonote::block blk;
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
- 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
- crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, 4),
+ last_version, last_version, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, last_version),
false, "Failed to generate block");
events.push_back(blk);
blk_last = blk;
@@ -213,6 +213,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
td.addr = miner_account.get_keys().m_account_address;
td.amount = amount_paid;
std::vector<tx_destination_entry> destinations;
+ // from v12, we need two outputs at least
+ destinations.push_back(td);
destinations.push_back(td);
if (pre_tx)
@@ -223,7 +225,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true, rct_config);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx)
@@ -237,6 +239,15 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
return true;
}
+bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
+ const int *out_idx, int mixin, uint64_t amount_paid, bool valid,
+ const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(transaction &tx)> &post_tx) const
+{
+ const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
+ return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx);
+}
+
bool gen_rct_tx_valid_from_pre_rct::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 2;
@@ -507,3 +518,11 @@ bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& event
NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed;
}
+bool gen_rct_tx_uses_output_too_early::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 10;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 2 };
+ return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL);
+}
diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h
index 00a2bd88c..9433d4b02 100644
--- a/tests/core_tests/rct.h
+++ b/tests/core_tests/rct.h
@@ -69,6 +69,10 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base
return true;
}
+ bool generate_with_full(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
+ uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
+ const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(cryptonote::transaction &tx)> &post_tx) const;
bool generate_with(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
uint64_t amount_paid, bool valid,
const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
@@ -262,3 +266,13 @@ struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base
};
template<> struct get_test_options<gen_rct_tx_rct_altered_extra>: public get_test_options<gen_rct_tx_validation_base> {};
+struct gen_rct_tx_uses_output_too_early : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_uses_output_too_early> {
+ const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(12, 69), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks, 0
+ };
+};
diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp
index 232c86482..acdfdc41b 100644
--- a/tests/core_tests/tx_validation.cpp
+++ b/tests/core_tests/tx_validation.cpp
@@ -529,7 +529,7 @@ bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector<test_event_en
// Tx with invalid key image can't be subscribed, so create empty signature
builder.m_tx.signatures.resize(1);
builder.m_tx.signatures[0].resize(1);
- builder.m_tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
+ builder.m_tx.signatures[0][0] = crypto::signature{};
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
@@ -562,7 +562,7 @@ bool gen_tx_key_image_is_invalid::generate(std::vector<test_event_entry>& events
// Tx with invalid key image can't be subscribed, so create empty signature
builder.m_tx.signatures.resize(1);
builder.m_tx.signatures[0].resize(1);
- builder.m_tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
+ builder.m_tx.signatures[0][0] = crypto::signature{};
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt
index fd49ba623..bc55da9e3 100644
--- a/tests/functional_tests/CMakeLists.txt
+++ b/tests/functional_tests/CMakeLists.txt
@@ -59,3 +59,7 @@ else()
message(WARNING "functional_tests_rpc skipped, needs the 'requests' python module")
set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc)
endif()
+
+add_test(
+ NAME check_missing_rpc_methods
+ COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}")
diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py
new file mode 100755
index 000000000..8d8711ffc
--- /dev/null
+++ b/tests/functional_tests/address_book.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+#encoding=utf-8
+
+# Copyright (c) 2019 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 wallet address book RPC
+"""
+
+from __future__ import print_function
+from framework.wallet import Wallet
+
+class AddressBookTest():
+ def run_test(self):
+ self.create()
+ self.test_address_book()
+
+ def create(self):
+ print('Creating wallet')
+ wallet = Wallet()
+ # close the wallet if any, will throw if none is loaded
+ try: wallet.close_wallet()
+ except: pass
+ 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'
+ res = wallet.restore_deterministic_wallet(seed = seed)
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.seed == seed
+
+ def test_address_book(self):
+ print('Testing address book')
+ wallet = Wallet()
+
+ # empty at start
+ res = wallet.get_address_book()
+ assert not 'entries' in res or (res.entries) == 0
+ ok = False
+ try: wallet.get_address_book([0])
+ except: ok = True
+ assert ok
+ ok = False
+ try: wallet.delete_address_book(0)
+ except: ok = True
+ assert ok
+ ok = False
+ try: wallet.edit_address_book(0, description = '')
+ except: ok = True
+ assert ok
+
+ # add one
+ res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self')
+ assert res.index == 0
+ for get_all in [True, False]:
+ res = wallet.get_address_book() if get_all else wallet.get_address_book([0])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 0
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert e.payment_id == '' or e.payment_id == '0' * 16 or e.payment_id == '0' * 64
+ assert e.description == 'self'
+
+ # add a duplicate
+ res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self')
+ assert res.index == 1
+ res = wallet.get_address_book()
+ assert len(res.entries) == 2
+ assert res.entries[0].index == 0
+ assert res.entries[1].index == 1
+ assert res.entries[0].address == res.entries[1].address
+ assert res.entries[0].payment_id == res.entries[1].payment_id
+ assert res.entries[0].description == res.entries[1].description
+ e = res.entries[1]
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ assert e == res.entries[0]
+
+ # request (partially) out of range
+ ok = False
+ try: res = wallet.get_address_book[4, 2]
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.get_address_book[0, 2]
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.get_address_book[2, 0]
+ except: ok = True
+ assert ok
+
+ # delete first
+ res = wallet.delete_address_book(0)
+ res = wallet.get_address_book()
+ assert len(res.entries) == 1
+ assert res.entries[0].index == 0
+ assert res.entries[0].address == e.address
+ assert res.entries[0].payment_id == e.payment_id
+ assert res.entries[0].description == e.description
+
+ # delete (new) first
+ res = wallet.delete_address_book(0)
+ res = wallet.get_address_book()
+ assert not 'entries' in res or (res.entries) == 0
+
+ # add non-addresses
+ errors = 0
+ try: wallet.add_address_book('', description = 'bad')
+ except: errors += 1
+ try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm ', description = 'bad')
+ except: errors += 1
+ try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn', description = 'bad')
+ except: errors += 1
+ try: wallet.add_address_book('9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB', description = 'bad')
+ except: errors += 1
+ try: wallet.add_address_book('donate@example.com', description = 'bad')
+ except: errors += 1
+ assert errors == 5
+ res = wallet.get_address_book()
+ assert not 'entries' in res or len(res.entries) == 0
+
+ # openalias
+ res = wallet.add_address_book('donate@getmonero.org', description = 'dev fund')
+ assert res.index == 0
+ res = wallet.get_address_book()
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
+ assert e.description == 'dev fund'
+
+ # UTF-8
+ res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = u'あまやかす')
+ assert res.index == 1
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ assert res.entries[0].description == u'あまやかす'
+ e = res.entries[0]
+
+ # duplicate request
+ res = wallet.get_address_book([1, 1])
+ assert len(res.entries) == 2
+ assert res.entries[0] == e
+ assert res.entries[1] == e
+
+ # payment IDs
+ res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 64)
+ assert res.index == 2
+ ok = False
+ try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = 'x' * 64)
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 65)
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 63)
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 16)
+ except: ok = True
+ assert ok
+
+ # various address types
+ res = wallet.make_integrated_address()
+ integrated_address = res.integrated_address
+ integrated_address_payment_id = res.payment_id
+ ok = False
+ try: res = wallet.add_address_book(integrated_address, payment_id = '0' * 64)
+ except: ok = True
+ assert ok
+ res = wallet.add_address_book(integrated_address)
+ assert res.index == 3
+ res = wallet.add_address_book('87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB')
+ assert res.index == 4
+
+ # get them back
+ res = wallet.get_address_book([0])
+ assert len(res.entries) == 1
+ assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
+ assert res.entries[0].description == 'dev fund'
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.entries[0].description == u'あまやかす'
+ res = wallet.get_address_book([2])
+ assert len(res.entries) == 1
+ assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ res = wallet.get_address_book([3])
+ assert len(res.entries) == 1
+ if False: # for now, the address book splits integrated addresses
+ assert res.entries[0].address == integrated_address
+ else:
+ assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.entries[0].payment_id == integrated_address_payment_id + '0' * 48
+ res = wallet.get_address_book([4])
+ assert len(res.entries) == 1
+ assert res.entries[0].address == '87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB'
+
+ # edit
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert e.payment_id == '0' * 64
+ assert e.description == u'あまやかす'
+ res = wallet.edit_address_book(1, payment_id = '1' * 64)
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert e.payment_id == '1' * 64
+ assert e.description == u'あまやかす'
+ res = wallet.edit_address_book(1, description = '')
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert e.payment_id == '1' * 64
+ assert e.description == ''
+ res = wallet.edit_address_book(1, description = 'えんしゅう')
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert e.payment_id == '1' * 64
+ assert e.description == u'えんしゅう'
+ res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A')
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
+ assert e.payment_id == '1' * 64
+ assert e.description == u'えんしゅう'
+ res = wallet.edit_address_book(1, payment_id = '')
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ e = res.entries[0]
+ assert e.index == 1
+ assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
+ assert e.payment_id == '0' * 64
+ assert e.description == u'えんしゅう'
+ ok = False
+ try: res = wallet.edit_address_book(1, address = '')
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.edit_address_book(1, payment_id = 'asdnd')
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.edit_address_book(1, address = 'address')
+ except: ok = True
+ assert ok
+ res = wallet.edit_address_book(1)
+ res = wallet.get_address_book([1])
+ assert len(res.entries) == 1
+ assert e == res.entries[0]
+
+ # empty
+ wallet.delete_address_book(4)
+ wallet.delete_address_book(0)
+ res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up
+ assert len(res.entries) == 1
+ assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
+ assert res.entries[0].description == u'えんしゅう'
+ wallet.delete_address_book(2)
+ wallet.delete_address_book(0)
+ wallet.delete_address_book(0)
+ res = wallet.get_address_book()
+ assert not 'entries' in res or len(res.entries) == 0
+
+
+if __name__ == '__main__':
+ AddressBookTest().run_test()
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
index 2c3f34c35..324af624a 100755
--- a/tests/functional_tests/blockchain.py
+++ b/tests/functional_tests/blockchain.py
@@ -53,7 +53,8 @@ class BlockchainTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def _test_generateblocks(self, blocks):
@@ -330,6 +331,9 @@ class BlockchainTest():
for txid in [alt_blocks[0], alt_blocks[2], alt_blocks[4]]:
assert len([chain for chain in res.chains if chain.block_hash == txid]) == 1
+ print('Saving blockchain explicitely')
+ daemon.save_bc()
+
if __name__ == '__main__':
BlockchainTest().run_test()
diff --git a/tests/functional_tests/check_missing_rpc_methods.py b/tests/functional_tests/check_missing_rpc_methods.py
new file mode 100644
index 000000000..6fadebf9b
--- /dev/null
+++ b/tests/functional_tests/check_missing_rpc_methods.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys
+import re
+
+USAGE = 'usage: check_untested_methods.py <rootdir>'
+try:
+ rootdir = sys.argv[1]
+except:
+ print(USAGE)
+ sys.exit(1)
+
+sys.path.insert(0, rootdir + '/utils/python-rpc')
+
+from framework import daemon
+from framework import wallet
+
+modules = [
+ {
+ 'name': 'daemon',
+ 'object': daemon.Daemon(),
+ 'path': rootdir + '/src/rpc/core_rpc_server.h',
+ 'ignore': []
+ },
+ {
+ 'name': 'wallet',
+ 'object': wallet.Wallet(),
+ 'path': rootdir + '/src/wallet/wallet_rpc_server.h',
+ 'ignore': []
+ }
+]
+
+error = False
+for module in modules:
+ for line in open(module['path']).readlines():
+ if 'MAP_URI_AUTO_JON2' in line or 'MAP_JON_RPC' in line:
+ match = re.search('.*\"(.*)\".*', line)
+ name = match.group(1)
+ if name in module['ignore'] or name.endswith('.bin'):
+ continue
+ if 'MAP_URI_AUTO_JON2' in line:
+ if not name.startswith('/'):
+ print('Error: %s does not start with /' % name)
+ error = True
+ name = name[1:]
+ if not hasattr(module['object'], name):
+ print('Error: %s API method %s does not have a matching function' % (module['name'], name))
+
+sys.exit(1 if error else 0)
diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py
index a722d8927..698217436 100755
--- a/tests/functional_tests/cold_signing.py
+++ b/tests/functional_tests/cold_signing.py
@@ -45,7 +45,8 @@ class ColdSigningTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self, idx):
@@ -93,7 +94,6 @@ class ColdSigningTest():
print("Creating transaction in hot wallet")
dst = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000}
- payment_id = '1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde'
self.hot_wallet.refresh()
res = self.hot_wallet.export_outputs()
@@ -101,7 +101,7 @@ class ColdSigningTest():
res = self.cold_wallet.export_key_images(True)
self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset)
- res = self.hot_wallet.transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False)
+ res = self.hot_wallet.transfer([dst], ring_size = 11, get_tx_key = False)
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert len(res.tx_key) == 0
@@ -123,7 +123,7 @@ class ColdSigningTest():
assert desc.amount_out == desc.amount_in - fee
assert desc.ring_size == 11
assert desc.unlock_time == 0
- assert desc.payment_id == payment_id
+ assert desc.payment_id in ['', '0000000000000000']
assert desc.change_amount == desc.amount_in - 1000000000000 - fee
assert desc.change_address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert desc.fee == fee
diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py
index 77d0e4c4d..9043565c7 100755
--- a/tests/functional_tests/functional_tests_rpc.py
+++ b/tests/functional_tests/functional_tests_rpc.py
@@ -10,7 +10,7 @@ import string
import os
USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-run> | all]'
-DEFAULT_TESTS = ['bans', 'daemon_info', 'blockchain', 'wallet_address', 'integrated_address', 'mining', 'transfer', 'txpool', 'multisig', 'cold_signing', 'sign_message', 'proofs', 'get_output_distribution']
+DEFAULT_TESTS = ['address_book', 'bans', 'blockchain', 'cold_signing', 'daemon_info', 'get_output_distribution', 'integrated_address', 'mining', 'multisig', 'proofs', 'sign_message', 'transfer', 'txpool', 'uri', 'validate_address', 'wallet']
try:
python = sys.argv[1]
srcdir = sys.argv[2]
@@ -36,9 +36,10 @@ except:
N_MONERODS = 1
N_WALLETS = 4
+WALLET_DIRECTORY = builddir + "/functional-tests-directory"
monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", "1", "--offline", "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--log-level", "1"]
-wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", builddir + "/functional-tests-directory", "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--daemon-port", "18180", "--log-level", "1"]
+wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--daemon-port", "18180", "--log-level", "1"]
command_lines = []
processes = []
@@ -62,6 +63,8 @@ try:
PYTHONPATH += ':'
PYTHONPATH += srcdir + '/../../utils/python-rpc'
os.environ['PYTHONPATH'] = PYTHONPATH
+ os.environ['WALLET_DIRECTORY'] = WALLET_DIRECTORY
+ os.environ['PYTHONIOENCODING'] = 'utf-8'
for i in range(len(command_lines)):
#print('Running: ' + str(command_lines[i]))
processes.append(subprocess.Popen(command_lines[i], stdout = outputs[i]))
@@ -133,6 +136,6 @@ else:
if len(FAIL) == 0:
print('Done, ' + str(len(PASS)) + '/' + str(len(tests)) + ' tests passed')
else:
- print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + string.join(FAIL, ', '))
+ print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + ', '.join(FAIL))
sys.exit(0 if len(FAIL) == 0 else 1)
diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py
index 93822e90a..077b094ba 100755
--- a/tests/functional_tests/get_output_distribution.py
+++ b/tests/functional_tests/get_output_distribution.py
@@ -44,7 +44,8 @@ class GetOutputDistributionTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py
index 5c14d34fd..a08a45ad4 100755
--- a/tests/functional_tests/mining.py
+++ b/tests/functional_tests/mining.py
@@ -46,12 +46,15 @@ class MiningTest():
def run_test(self):
self.reset()
self.create()
- self.mine()
+ self.mine(True)
+ self.mine(False)
+ self.submitblock()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@@ -62,8 +65,8 @@ class MiningTest():
except: pass
res = wallet.restore_deterministic_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')
- def mine(self):
- print("Test mining")
+ def mine(self, via_daemon):
+ print("Test mining via " + ("daemon" if via_daemon else "wallet"))
daemon = Daemon()
wallet = Wallet()
@@ -76,7 +79,10 @@ class MiningTest():
res_status = daemon.mining_status()
- res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1)
+ if via_daemon:
+ res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1)
+ else:
+ res = wallet.start_mining(threads_count = 1)
res_status = daemon.mining_status()
assert res_status.active == True
@@ -101,7 +107,11 @@ class MiningTest():
timeout -= 1
assert timeout >= 0
- res = daemon.stop_mining()
+ if via_daemon:
+ res = daemon.stop_mining()
+ else:
+ res = wallet.stop_mining()
+
res_status = daemon.mining_status()
assert res_status.active == False
@@ -113,7 +123,10 @@ class MiningTest():
balance = res_getbalance.balance
assert balance >= prev_balance + (new_height - prev_height) * 600000000000
- res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True)
+ if via_daemon:
+ res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True)
+ else:
+ res = wallet.start_mining(threads_count = 1, do_background_mining = True)
res_status = daemon.mining_status()
assert res_status.active == True
assert res_status.threads_count == 1
@@ -122,10 +135,40 @@ class MiningTest():
assert res_status.block_reward >= 600000000000
# don't wait, might be a while if the machine is busy, which it probably is
- res = daemon.stop_mining()
+ if via_daemon:
+ res = daemon.stop_mining()
+ else:
+ res = wallet.stop_mining()
res_status = daemon.mining_status()
assert res_status.active == False
+ def submitblock(self):
+ print("Test submitblock")
+
+ daemon = Daemon()
+ res = daemon.get_height()
+ height = res.height
+ res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 5)
+ assert len(res.blocks) == 5
+ hashes = res.blocks
+ blocks = []
+ for block_hash in hashes:
+ res = daemon.getblock(hash = block_hash)
+ assert len(res.blob) > 0 and len(res.blob) % 2 == 0
+ blocks.append(res.blob)
+ res = daemon.get_height()
+ assert res.height == height + 5
+ res = daemon.pop_blocks(5)
+ res = daemon.get_height()
+ assert res.height == height
+ for i in range(len(hashes)):
+ block_hash = hashes[i]
+ assert len(block_hash) == 64
+ res = daemon.submitblock(blocks[i])
+ res = daemon.get_height()
+ assert res.height == height + i + 1
+ assert res.hash == block_hash
+
if __name__ == '__main__':
MiningTest().run_test()
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index b109acf91..e4e908aa1 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -46,6 +46,8 @@ class MultisigTest():
self.mine('4ADHswEU3XBUee8pudBkZQd9beJainqNo1BQKkHJujAEPJyQrLj9U4dNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRUDxgjW', 5)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60)
+ self.test_states()
+
self.create_multisig_wallets(2, 2, '493DsrfJPqiN3Suv9RcRDoZEbQtKZX1sNcGPA3GhkKYEEmivk8kjQrTdRdVc4ZbmzWJuE157z9NNUKmF2VDfdYDR3CziGMk')
self.import_multisig_info([1, 0], 5)
txid = self.transfer([1, 0])
@@ -79,7 +81,8 @@ class MultisigTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def mine(self, address, blocks):
@@ -152,6 +155,72 @@ class MultisigTest():
assert res.threshold == M_threshold
assert res.total == N_total
+ def test_states(self):
+ print('Testing multisig states')
+ seeds = [
+ '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',
+ 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout',
+ 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid',
+ ]
+ info = []
+ wallet = [None, None, None]
+ for i in range(3):
+ wallet[i] = Wallet(idx = i)
+ try: wallet[i].close_wallet()
+ except: pass
+ res = wallet[i].restore_deterministic_wallet(seed = seeds[i])
+ res = wallet[i].is_multisig()
+ assert not res.multisig
+ res = wallet[i].prepare_multisig()
+ assert len(res.multisig_info) > 0
+ info.append(res.multisig_info)
+
+ for i in range(3):
+ ok = False
+ try: res = wallet[i].finalize_multisig(info)
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet[i].exchange_multisig_keys(info)
+ except: ok = True
+ assert ok
+ res = wallet[i].is_multisig()
+ assert not res.multisig
+
+ res = wallet[0].make_multisig(info[0:2], 2)
+ res = wallet[0].is_multisig()
+ assert res.multisig
+ assert res.ready
+
+ ok = False
+ try: res = wallet[0].finalize_multisig(info)
+ except: ok = True
+ assert ok
+
+ ok = False
+ try: res = wallet[0].prepare_multisig()
+ except: ok = True
+ assert ok
+
+ ok = False
+ try: res = wallet[0].make_multisig(info[0:2], 2)
+ except: ok = True
+ assert ok
+
+ res = wallet[1].make_multisig(info, 2)
+ res = wallet[1].is_multisig()
+ assert res.multisig
+ assert not res.ready
+
+ ok = False
+ try: res = wallet[1].prepare_multisig()
+ except: ok = True
+ assert ok
+
+ ok = False
+ try: res = wallet[1].make_multisig(info[0:2], 2)
+ except: ok = True
+ assert ok
def import_multisig_info(self, signers, expected_outputs):
assert len(signers) >= 2
@@ -203,7 +272,7 @@ class MultisigTest():
assert desc.amount_out == desc.amount_in - fee
assert desc.ring_size == 11
assert desc.unlock_time == 0
- assert desc.payment_id == '0000000000000000'
+ assert not 'payment_id' in desc or desc.payment_id in ['', '0000000000000000']
assert desc.change_amount == desc.amount_in - 1000000000000 - fee
assert desc.change_address == self.wallet_address
assert desc.fee == fee
diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py
index 243929dc3..7beb3ec6e 100755
--- a/tests/functional_tests/proofs.py
+++ b/tests/functional_tests/proofs.py
@@ -44,12 +44,14 @@ class ProofsTest():
txid, tx_key, amount = self.transfer()
self.check_tx_key(txid, tx_key, amount)
self.check_tx_proof(txid, amount)
+ self.check_spend_proof(txid)
self.check_reserve_proof()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def mine(self, address, blocks):
@@ -217,6 +219,40 @@ class ProofsTest():
except: ok = True
assert ok or not res.good
+ def check_spend_proof(self, txid):
+ daemon = Daemon()
+
+ print('Checking spend proof')
+
+ self.wallet[0].refresh()
+ self.wallet[1].refresh()
+
+ res = self.wallet[0].get_spend_proof(txid, message = 'foo')
+ assert len(res.signature) > 0
+ signature = res.signature
+ res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature)
+ assert res.good
+
+ res = self.wallet[0].get_spend_proof(txid, message = 'foobar')
+ assert len(res.signature) > 0
+ signature2 = res.signature
+ res = self.wallet[1].check_spend_proof(txid, message = 'foobar', signature = signature2)
+ assert res.good
+
+ ok = False
+ try: res = self.wallet[1].check_spend_proof('0' * 64, message = 'foo', signature = signature)
+ except: ok = True
+ assert ok or not res.good
+
+ ok = False
+ try: res = self.wallet[1].check_spend_proof(txid, message = 'bar', signature = signature)
+ except: ok = True
+ assert ok or not res.good
+
+ ok = False
+ try: res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature2)
+ except: ok = True
+ assert ok or not res.good
def check_reserve_proof(self):
daemon = Daemon()
diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py
index ed1e332e9..71be785b8 100755
--- a/tests/functional_tests/speed.py
+++ b/tests/functional_tests/speed.py
@@ -47,14 +47,27 @@ from framework.wallet import Wallet
class SpeedTest():
- def set_test_params(self):
- self.num_nodes = 1
+ def reset(self):
+ print('Resetting blockchain')
+ daemon = Daemon()
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
+ daemon.flush_txpool()
def run_test(self):
+ self.reset()
+
daemon = Daemon()
wallet = Wallet()
- destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1,3)
+ # close the wallet if any, will throw if none is loaded
+ try: wallet.close_wallet()
+ except: pass
+ wallet.restore_deterministic_wallet('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')
+
+ destinations = []
+ for i in range(3):
+ destinations.append({"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'})
self._test_speed_generateblocks(daemon=daemon, blocks=70)
for i in range(1, 10):
@@ -69,7 +82,6 @@ class SpeedTest():
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')
@@ -77,7 +89,7 @@ class SpeedTest():
print('Test speed of transfer')
start = time.time()
- destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1)
+ destinations = [{"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}]
res = wallet.transfer_split(destinations)
print('generating tx took: ', time.time() - start, 'seconds')
diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp
index 2add66b8b..218590a27 100644
--- a/tests/functional_tests/transactions_flow_test.cpp
+++ b/tests/functional_tests/transactions_flow_test.cpp
@@ -253,7 +253,7 @@ bool transactions_flow_test(std::string& working_folder,
transfered_money += amount_to_tx;
LOG_PRINT_L0("transferred " << amount_to_tx << ", i=" << i );
- tx_test_entry& ent = txs[get_transaction_hash(tx)] = boost::value_initialized<tx_test_entry>();
+ tx_test_entry& ent = txs[get_transaction_hash(tx)] = tx_test_entry{};
ent.amount_transfered = amount_to_tx;
ent.tx = tx;
//if(i % transactions_per_second)
diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py
index 7ebda6ebd..0c942f48b 100755
--- a/tests/functional_tests/transfer.py
+++ b/tests/functional_tests/transfer.py
@@ -44,14 +44,20 @@ class TransferTest():
self.mine()
self.transfer()
self.check_get_bulk_payments()
+ self.check_get_payments()
self.check_double_spend_detection()
+ self.sweep_dust()
self.sweep_single()
self.check_destinations()
+ self.check_tx_notes()
+ self.check_rescan()
+ self.check_is_key_image_spent()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@@ -108,13 +114,19 @@ class TransferTest():
except: ok = True
assert ok
+ print ('Checking long payment IDs are rejected')
+ ok = False
+ try: self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False, get_tx_hex = True)
+ except: ok = True
+ assert ok
+
print ('Checking empty destination is rejected')
ok = False
try: self.wallet[0].transfer([], ring_size = 11, get_tx_key = False)
except: ok = True
assert ok
- res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False)
+ res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = False, get_tx_hex = True)
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert len(res.tx_key) == 0
@@ -122,12 +134,19 @@ class TransferTest():
amount = res.amount
assert res.fee > 0
fee = res.fee
- assert len(res.tx_blob) == 0
+ assert len(res.tx_blob) > 0
+ blob_size = len(res.tx_blob) // 2
assert len(res.tx_metadata) == 0
assert len(res.multisig_txset) == 0
assert len(res.unsigned_txset) == 0
unsigned_txset = res.unsigned_txset
+ res = daemon.get_fee_estimate(10)
+ assert res.fee > 0
+ assert res.quantization_mask > 0
+ expected_fee = (res.fee * 1 * blob_size + res.quantization_mask - 1) // res.quantization_mask * res.quantization_mask
+ assert abs(1 - fee / expected_fee) < 0.01
+
self.wallet[0].refresh()
res = daemon.get_info()
@@ -143,7 +162,7 @@ class TransferTest():
assert e.type == 'block'
e = res.pending[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ['', '0000000000000000']
assert e.type == 'pending'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -176,7 +195,7 @@ class TransferTest():
assert e.type == 'block'
e = res.out[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ['', '0000000000000000']
assert e.type == 'out'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -192,7 +211,7 @@ class TransferTest():
assert res.transfers[0] == res.transfer
t = res.transfer
assert t.txid == txid
- assert t.payment_id == payment_id
+ assert t.payment_id in ['', '0000000000000000']
assert t.height == wallet_height - 1
assert t.timestamp > 0
assert t.amount == 0 # to self, so it's just "pay a fee" really
@@ -214,7 +233,7 @@ class TransferTest():
print("Creating transfer to another, manual relay")
dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1000000000000}
- res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = True, do_not_relay = True, get_tx_hex = True)
+ res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = True, do_not_relay = True, get_tx_hex = True)
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert len(res.tx_key) == 32*2
@@ -256,7 +275,7 @@ class TransferTest():
assert not 'failed' in res or len(res.failed) == 0
e = res.pool[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ["", "0000000000000000"] # long payment IDs are now ignored
assert e.type == 'pool'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -282,7 +301,7 @@ class TransferTest():
assert not 'failed' in res or len(res.failed) == 0
e = res['in'][0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ["", "0000000000000000"] # long payment IDs are now ignored
assert e.type == 'in'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -305,7 +324,7 @@ class TransferTest():
dst0 = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000}
dst1 = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1100000000000}
dst2 = {'address': '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 'amount': 1200000000000}
- res = self.wallet[0].transfer([dst0, dst1, dst2], ring_size = 11, payment_id = payment_id, get_tx_key = True)
+ res = self.wallet[0].transfer([dst0, dst1, dst2], ring_size = 11, get_tx_key = True)
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert len(res.tx_key) == 32*2
@@ -344,7 +363,7 @@ class TransferTest():
assert len(e) == 1
e = e[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ["", "0000000000000000"] # long payment IDs are now ignored
assert e.type == 'out'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -372,7 +391,7 @@ class TransferTest():
assert len(e) == 1
e = e[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ["", "0000000000000000"] # long payment IDs are now ignored
assert e.type == 'in'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -399,7 +418,7 @@ class TransferTest():
assert len(e) == 1
e = e[0]
assert e.txid == txid
- assert e.payment_id == payment_id
+ assert e.payment_id in ["", "0000000000000000"] # long payment IDs are now ignored
assert e.type == 'in'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
@@ -508,7 +527,7 @@ class TransferTest():
res = self.wallet[1].get_bulk_payments()
assert len(res.payments) >= 3 # two txes to standard address were sent, plus one to integrated address
res = self.wallet[1].get_bulk_payments(payment_ids = ['1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde'])
- assert len(res.payments) >= 2 # two txes were sent with that payment id
+ assert not 'payments' in res or len(res.payments) == 0 # long payment IDs are now ignored on receipt
res = self.wallet[1].get_bulk_payments(payment_ids = ['ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'])
assert 'payments' not in res or len(res.payments) == 0 # none with that payment id
res = self.wallet[1].get_bulk_payments(payment_ids = ['1111111122222222' + '0'*48])
@@ -518,11 +537,33 @@ class TransferTest():
res = self.wallet[2].get_bulk_payments()
assert len(res.payments) >= 1 # one tx was sent
res = self.wallet[2].get_bulk_payments(payment_ids = ['1'*64, '1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde', '2'*64])
- assert len(res.payments) >= 1 # one tx was sent
+ assert not 'payments' in res or len(res.payments) == 0 # long payment IDs are now ignored
res = self.wallet[1].get_bulk_payments(["1111111122222222"])
assert len(res.payments) >= 1 # we have one of these
+ def check_get_payments(self):
+ print('Checking get_payments')
+
+ daemon = Daemon()
+ res = daemon.get_info()
+ height = res.height
+
+ self.wallet[0].refresh()
+ self.wallet[1].refresh()
+
+ res = self.wallet[0].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde')
+ assert 'payments' not in res or len(res.payments) == 0
+
+ res = self.wallet[1].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde')
+ assert 'payments' not in res or len(res.payments) == 0
+
+ res = self.wallet[1].get_payments('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+ assert 'payments' not in res or len(res.payments) == 0
+
+ res = self.wallet[1].get_payments(payment_id = '1111111122222222' + '0'*48)
+ assert len(res.payments) >= 1 # one tx to integrated address
+
def check_double_spend_detection(self):
print('Checking double spend detection')
txes = [[None, None], [None, None]]
@@ -583,6 +624,13 @@ class TransferTest():
assert tx.in_pool
assert tx.double_spend_seen
+ def sweep_dust(self):
+ print("Sweeping dust")
+ daemon = Daemon()
+ self.wallet[0].refresh()
+ res = self.wallet[0].sweep_dust()
+ assert not 'tx_hash_list' in res or len(res.tx_hash_list) == 0 # there's just one, but it cannot meet the fee
+
def sweep_single(self):
daemon = Daemon()
@@ -603,11 +651,19 @@ class TransferTest():
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
balance = res.balance
- res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ res = daemon.is_key_image_spent([ki])
+ assert len(res.spent_status) == 1
+ assert res.spent_status[0] == 0
res = self.wallet[0].sweep_single('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', key_image = ki)
assert len(res.tx_hash) == 64
tx_hash = res.tx_hash
+ res = daemon.is_key_image_spent([ki])
+ assert len(res.spent_status) == 1
+ assert res.spent_status[0] == 2
daemon.generateblocks('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 1)
+ res = daemon.is_key_image_spent([ki])
+ assert len(res.spent_status) == 1
+ assert res.spent_status[0] == 1
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
new_balance = res.balance
@@ -687,7 +743,97 @@ class TransferTest():
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
self.wallet[0].refresh()
+ def check_tx_notes(self):
+ daemon = Daemon()
+ print('Testing tx notes')
+ res = self.wallet[0].get_transfers()
+ assert len(res['in']) > 0
+ in_txid = res['in'][0].txid
+ assert len(res['out']) > 0
+ out_txid = res['out'][0].txid
+ res = self.wallet[0].get_tx_notes([in_txid, out_txid])
+ assert res.notes == ['', '']
+ res = self.wallet[0].set_tx_notes([in_txid, out_txid], ['in txid', 'out txid'])
+ res = self.wallet[0].get_tx_notes([in_txid, out_txid])
+ assert res.notes == ['in txid', 'out txid']
+ res = self.wallet[0].get_tx_notes([out_txid, in_txid])
+ assert res.notes == ['out txid', 'in txid']
+
+ def check_rescan(self):
+ daemon = Daemon()
+
+ print('Testing rescan_spent')
+ res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ transfers = res.transfers
+ res = self.wallet[0].rescan_spent()
+ res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ assert transfers == res.transfers
+
+ for hard in [False, True]:
+ print('Testing %s rescan_blockchain' % ('hard' if hard else 'soft'))
+ res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ transfers = res.transfers
+ res = self.wallet[0].get_transfers()
+ t_in = res['in']
+ t_out = res.out
+ res = self.wallet[0].rescan_blockchain(hard = hard)
+ res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ assert transfers == res.transfers
+ res = self.wallet[0].get_transfers()
+ assert t_in == res['in']
+ # some information can not be recovered for out txes
+ unrecoverable_fields = ['payment_id', 'destinations', 'note']
+ old_t_out = []
+ for x in t_out:
+ e = {}
+ for k in x.keys():
+ if not k in unrecoverable_fields:
+ e[k] = x[k]
+ old_t_out.append(e)
+ new_t_out = []
+ for x in res.out:
+ e = {}
+ for k in x.keys():
+ if not k in unrecoverable_fields:
+ e[k] = x[k]
+ new_t_out.append(e)
+ assert sorted(old_t_out, key = lambda k: k['txid']) == sorted(new_t_out, key = lambda k: k['txid'])
+
+ def check_is_key_image_spent(self):
+ daemon = Daemon()
+
+ print('Testing is_key_image_spent')
+ res = self.wallet[0].incoming_transfers(transfer_type = 'all')
+ transfers = res.transfers
+ ki = [x.key_image for x in transfers]
+ expected = [1 if x.spent else 0 for x in transfers]
+ res = daemon.is_key_image_spent(ki)
+ assert res.spent_status == expected
+
+ res = self.wallet[0].incoming_transfers(transfer_type = 'available')
+ transfers = res.transfers
+ ki = [x.key_image for x in transfers]
+ expected = [0 for x in transfers]
+ res = daemon.is_key_image_spent(ki)
+ assert res.spent_status == expected
+
+ res = self.wallet[0].incoming_transfers(transfer_type = 'unavailable')
+ transfers = res.transfers
+ ki = [x.key_image for x in transfers]
+ expected = [1 for x in transfers]
+ res = daemon.is_key_image_spent(ki)
+ assert res.spent_status == expected
+
+ ki = [ki[-1]] * 5
+ expected = [1] * len(ki)
+ res = daemon.is_key_image_spent(ki)
+ assert res.spent_status == expected
+
+ ki = ['2'*64, '1'*64]
+ expected = [0, 0]
+ res = daemon.is_key_image_spent(ki)
+ assert res.spent_status == expected
if __name__ == '__main__':
diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py
index b6af4c84f..27ae89764 100755
--- a/tests/functional_tests/txpool.py
+++ b/tests/functional_tests/txpool.py
@@ -46,7 +46,8 @@ class TransferTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@@ -81,6 +82,26 @@ class TransferTest():
return txes
+ def check_empty_pool(self):
+ daemon = Daemon()
+
+ res = daemon.get_transaction_pool_hashes()
+ assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
+ res = daemon.get_transaction_pool_stats()
+ assert res.pool_stats.bytes_total == 0
+ assert res.pool_stats.bytes_min == 0
+ assert res.pool_stats.bytes_max == 0
+ assert res.pool_stats.bytes_med == 0
+ assert res.pool_stats.fee_total == 0
+ assert res.pool_stats.oldest == 0
+ assert res.pool_stats.txs_total == 0
+ assert res.pool_stats.num_failing == 0
+ assert res.pool_stats.num_10m == 0
+ assert res.pool_stats.num_not_relayed == 0
+ assert res.pool_stats.histo_98pc == 0
+ assert not 'histo' in res.pool_stats or len(res.pool_stats.histo) == 0
+ assert res.pool_stats.num_double_spends == 0
+
def check_txpool(self):
daemon = Daemon()
wallet = Wallet()
@@ -89,6 +110,8 @@ class TransferTest():
height = res.height
txpool_size = res.tx_pool_size
+ self.check_empty_pool()
+
txes = self.create_txes('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 5)
res = daemon.get_info()
@@ -97,6 +120,10 @@ class TransferTest():
res = daemon.get_transaction_pool()
assert len(res.transactions) == txpool_size
+ total_bytes = 0
+ total_fee = 0
+ min_bytes = 99999999999999
+ max_bytes = 0
for txid in txes.keys():
x = [x for x in res.transactions if x.id_hash == txid]
assert len(x) == 1
@@ -110,9 +137,26 @@ class TransferTest():
assert x.fee == txes[txid].fee
assert x.tx_blob == txes[txid].tx_blob
+ total_bytes += x.blob_size
+ total_fee += x.fee
+ min_bytes = min(min_bytes, x.blob_size)
+ max_bytes = max(max_bytes, x.blob_size)
+
res = daemon.get_transaction_pool_hashes()
assert sorted(res.tx_hashes) == sorted(txes.keys())
+ res = daemon.get_transaction_pool_stats()
+ assert res.pool_stats.bytes_total == total_bytes
+ assert res.pool_stats.bytes_min == min_bytes
+ assert res.pool_stats.bytes_max == max_bytes
+ assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes
+ assert res.pool_stats.fee_total == total_fee
+ assert res.pool_stats.txs_total == len(txes)
+ assert res.pool_stats.num_failing == 0
+ assert res.pool_stats.num_10m == 0
+ assert res.pool_stats.num_not_relayed == 0
+ assert res.pool_stats.num_double_spends == 0
+
print('Flushing 2 transactions')
txes_keys = list(txes.keys())
daemon.flush_txpool([txes_keys[1], txes_keys[3]])
@@ -127,6 +171,42 @@ class TransferTest():
res = daemon.get_transaction_pool_hashes()
assert sorted(res.tx_hashes) == sorted(new_keys)
+ res = daemon.get_transaction_pool()
+ assert len(res.transactions) == len(new_keys)
+ total_bytes = 0
+ total_fee = 0
+ min_bytes = 99999999999999
+ max_bytes = 0
+ for txid in new_keys:
+ x = [x for x in res.transactions if x.id_hash == txid]
+ assert len(x) == 1
+ x = x[0]
+ assert x.kept_by_block == False
+ assert x.last_failed_id_hash == '0'*64
+ assert x.double_spend_seen == False
+ assert x.weight >= x.blob_size
+
+ assert x.blob_size * 2 == len(txes[txid].tx_blob)
+ assert x.fee == txes[txid].fee
+ assert x.tx_blob == txes[txid].tx_blob
+
+ total_bytes += x.blob_size
+ total_fee += x.fee
+ min_bytes = min(min_bytes, x.blob_size)
+ max_bytes = max(max_bytes, x.blob_size)
+
+ res = daemon.get_transaction_pool_stats()
+ assert res.pool_stats.bytes_total == total_bytes
+ assert res.pool_stats.bytes_min == min_bytes
+ assert res.pool_stats.bytes_max == max_bytes
+ assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes
+ assert res.pool_stats.fee_total == total_fee
+ assert res.pool_stats.txs_total == len(new_keys)
+ assert res.pool_stats.num_failing == 0
+ assert res.pool_stats.num_10m == 0
+ assert res.pool_stats.num_not_relayed == 0
+ assert res.pool_stats.num_double_spends == 0
+
print('Flushing unknown transactions')
unknown_txids = ['1'*64, '2'*64, '3'*64]
daemon.flush_txpool(unknown_txids)
@@ -140,6 +220,8 @@ class TransferTest():
res = daemon.get_transaction_pool_hashes()
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
+ self.check_empty_pool()
+
print('Popping block')
daemon.pop_blocks(1)
res = daemon.get_transaction_pool_hashes()
@@ -159,6 +241,9 @@ class TransferTest():
assert x.fee == txes[txid].fee
assert x.tx_blob == txes[txid].tx_blob
+ daemon.flush_txpool()
+ self.check_empty_pool()
+
if __name__ == '__main__':
TransferTest().run_test()
diff --git a/tests/functional_tests/uri.py b/tests/functional_tests/uri.py
new file mode 100755
index 000000000..f759b316a
--- /dev/null
+++ b/tests/functional_tests/uri.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python3
+#encoding=utf-8
+
+# Copyright (c) 2019 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 URI RPC
+"""
+
+from __future__ import print_function
+try:
+ from urllib import quote as urllib_quote
+except:
+ from urllib.parse import quote as urllib_quote
+
+from framework.wallet import Wallet
+
+class URITest():
+ def run_test(self):
+ self.create()
+ self.test_monero_uri()
+
+ def create(self):
+ print('Creating wallet')
+ wallet = Wallet()
+ # close the wallet if any, will throw if none is loaded
+ try: wallet.close_wallet()
+ except: pass
+ 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'
+ res = wallet.restore_deterministic_wallet(seed = seed)
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.seed == seed
+
+ def test_monero_uri(self):
+ print('Testing monero: URI')
+ wallet = Wallet()
+
+ utf8string = [u'えんしゅう', u'あまやかす']
+ quoted_utf8string = [urllib_quote(x.encode('utf8')) for x in utf8string]
+
+ ok = False
+ try: res = wallet.make_uri()
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.make_uri(address = '')
+ except: ok = True
+ assert ok
+ ok = False
+ try: res = wallet.make_uri(address = 'kjshdkj')
+ except: ok = True
+ assert ok
+
+ for address in [
+ '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
+ '4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY',
+ '8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm'
+ ]:
+ res = wallet.make_uri(address = address)
+ assert res.uri == 'monero:' + address
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 0
+ assert res.uri.tx_description == ''
+ assert res.uri.recipient_name == ''
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+ res = wallet.make_uri(address = address, amount = 11000000000)
+ assert res.uri == 'monero:' + address + '?tx_amount=0.011' or res.uri == 'monero:' + address + '?tx_amount=0.011000000000'
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 11000000000
+ assert res.uri.tx_description == ''
+ assert res.uri.recipient_name == ''
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+
+ res = wallet.make_uri(address = address, tx_description = utf8string[0])
+ assert res.uri == 'monero:' + address + '?tx_description=' + quoted_utf8string[0]
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 0
+ assert res.uri.tx_description == utf8string[0]
+ assert res.uri.recipient_name == ''
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ res = wallet.make_uri(address = address, recipient_name = utf8string[0])
+ assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0]
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 0
+ assert res.uri.tx_description == ''
+ assert res.uri.recipient_name == utf8string[0]
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1])
+ assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 0
+ assert res.uri.tx_description == utf8string[1]
+ assert res.uri.recipient_name == utf8string[0]
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000)
+ assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 1000000000000
+ assert res.uri.tx_description == utf8string[1]
+ assert res.uri.recipient_name == utf8string[0]
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000, payment_id = '1' * 64)
+ assert res.uri == 'monero:' + address + '?tx_payment_id=' + '1' * 64 + '&tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == '1' * 64
+ assert res.uri.amount == 1000000000000
+ assert res.uri.tx_description == utf8string[1]
+ assert res.uri.recipient_name == utf8string[0]
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ # spaces must be encoded as %20
+ res = wallet.make_uri(address = address, tx_description = ' ' + utf8string[1] + ' ' + utf8string[0] + ' ', amount = 1000000000000)
+ assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&tx_description=%20' + quoted_utf8string[1] + '%20' + quoted_utf8string[0] + '%20'
+ res = wallet.parse_uri(res.uri)
+ assert res.uri.address == address
+ assert res.uri.payment_id == ''
+ assert res.uri.amount == 1000000000000
+ assert res.uri.tx_description == ' ' + utf8string[1] + ' ' + utf8string[0] + ' '
+ assert res.uri.recipient_name == ''
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ # the example from the docs
+ res = wallet.parse_uri('monero:46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_amount=239.39014&tx_description=donation')
+ assert res.uri.address == '46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em'
+ assert res.uri.amount == 239390140000000
+ assert res.uri.tx_description == 'donation'
+ assert res.uri.recipient_name == ''
+ assert res.uri.payment_id == ''
+ assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
+
+ # malformed/invalid
+ for uri in [
+ '',
+ ':',
+ 'monero',
+ 'notmonero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
+ 'MONERO:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
+ 'MONERO::42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
+ 'monero:',
+ 'monero:badaddress',
+ 'monero:tx_amount=10',
+ 'monero:?tx_amount=10',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=-1',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1e12',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=+12',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1+2',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=A',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=0x2',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=222222222222222222222',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn?tx_amount=10',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&foo=bar',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=10&tx_amount=20',
+ 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_payment_id=1111111111111111',
+ 'monero:4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY?tx_payment_id=' + '1' * 64,
+ 'monero:9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB',
+ 'monero:5K8mwfjumVseCcQEjNbf59Um6R9NfVUNkHTLhhPCmNvgDLVS88YW5tScnm83rw9mfgYtchtDDTW5jEfMhygi27j1QYphX38hg6m4VMtN29',
+ 'monero:7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF',
+ ]:
+ ok = False
+ try: res = wallet.parse_uri(uri)
+ except: ok = True
+ assert ok, res
+
+ # unknown parameters but otherwise valid
+ res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar')
+ assert res.uri.address == address
+ assert res.uri.amount == 239390140000000
+ assert res.unknown_parameters == ['foo=bar'], res
+ res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar&baz=quux')
+ assert res.uri.address == address
+ assert res.uri.amount == 239390140000000
+ assert res.unknown_parameters == ['foo=bar', 'baz=quux'], res
+ res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&%20=%20')
+ assert res.uri.address == address
+ assert res.uri.amount == 239390140000000
+ assert res.unknown_parameters == ['%20=%20'], res
+ res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&unknown=' + quoted_utf8string[0])
+ assert res.uri.address == address
+ assert res.uri.amount == 239390140000000
+ assert res.unknown_parameters == [u'unknown=' + quoted_utf8string[0]], res
+
+
+
+if __name__ == '__main__':
+ URITest().run_test()
diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py
index 58748b0a2..7c3d8abfa 100755
--- a/tests/functional_tests/validate_address.py
+++ b/tests/functional_tests/validate_address.py
@@ -28,11 +28,10 @@
# 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 time
-
"""Test address validation RPC calls
"""
+from __future__ import print_function
from framework.wallet import Wallet
class AddressValidationTest():
diff --git a/tests/functional_tests/wallet_address.py b/tests/functional_tests/wallet.py
index eda52b432..5bb3ec80a 100755
--- a/tests/functional_tests/wallet_address.py
+++ b/tests/functional_tests/wallet.py
@@ -29,31 +29,54 @@
# 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 transaction creation RPC calls
-
-Test the following RPCs:
- - [TODO: many tests still need to be written]
-
+"""Test basic wallet functionality
"""
from __future__ import print_function
+import sys
+import os
+import errno
+
from framework.wallet import Wallet
from framework.daemon import Daemon
-class WalletAddressTest():
+class WalletTest():
def run_test(self):
self.reset()
self.create()
self.check_main_address()
self.check_keys()
self.create_subaddresses()
+ self.tags()
+ self.attributes()
self.open_close()
self.languages()
+ self.change_password()
+ self.store()
+
+ def remove_file(self, name):
+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+ assert WALLET_DIRECTORY != ''
+ try:
+ os.unlink(WALLET_DIRECTORY + '/' + name)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+ def remove_wallet_files(self, name):
+ for suffix in ['', '.keys']:
+ self.remove_file(name + suffix)
+
+ def file_exists(self, name):
+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+ assert WALLET_DIRECTORY != ''
+ return os.path.isfile(WALLET_DIRECTORY + '/' + name)
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
- daemon.pop_blocks(1000)
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@@ -158,6 +181,103 @@ class WalletAddressTest():
res = wallet.get_address_index('82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf')
assert res.index == {'major': 1, 'minor': 0}
+ res = wallet.label_account(0, "main")
+
+ def tags(self):
+ print('Testing tags')
+ wallet = Wallet()
+ res = wallet.get_account_tags()
+ assert not 'account_tags' in res or len(res.account_tags) == 0
+ ok = False
+ try: res = wallet.get_accounts('tag')
+ except: ok = True
+ assert ok or not 'subaddress_accounts' in res or res.subaddress_accounts == 0
+ wallet.tag_accounts('tag0', [1])
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 1
+ assert res.account_tags[0].tag == 'tag0'
+ assert res.account_tags[0].label == ''
+ assert res.account_tags[0].accounts == [1]
+ res = wallet.get_accounts('tag0')
+ assert len(res.subaddress_accounts) == 1
+ assert res.subaddress_accounts[0].account_index == 1
+ assert res.subaddress_accounts[0].base_address == '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf'
+ assert res.subaddress_accounts[0].balance == 0
+ assert res.subaddress_accounts[0].unlocked_balance == 0
+ assert res.subaddress_accounts[0].label == 'idx1_new'
+ assert res.subaddress_accounts[0].tag == 'tag0'
+ wallet.untag_accounts([0])
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 1
+ assert res.account_tags[0].tag == 'tag0'
+ assert res.account_tags[0].label == ''
+ assert res.account_tags[0].accounts == [1]
+ wallet.untag_accounts([1])
+ res = wallet.get_account_tags()
+ assert not 'account_tags' in res or len(res.account_tags) == 0
+ wallet.tag_accounts('tag0', [0])
+ wallet.tag_accounts('tag1', [1])
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 2
+ x = [x for x in res.account_tags if x.tag == 'tag0']
+ assert len(x) == 1
+ assert x[0].tag == 'tag0'
+ assert x[0].label == ''
+ assert x[0].accounts == [0]
+ x = [x for x in res.account_tags if x.tag == 'tag1']
+ assert len(x) == 1
+ assert x[0].tag == 'tag1'
+ assert x[0].label == ''
+ assert x[0].accounts == [1]
+ wallet.tag_accounts('tagA', [0, 1])
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 1
+ assert res.account_tags[0].tag == 'tagA'
+ assert res.account_tags[0].label == ''
+ assert res.account_tags[0].accounts == [0, 1]
+ wallet.tag_accounts('tagB', [1, 0])
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 1
+ assert res.account_tags[0].tag == 'tagB'
+ assert res.account_tags[0].label == ''
+ assert res.account_tags[0].accounts == [0, 1]
+ wallet.set_account_tag_description('tagB', 'tag B')
+ res = wallet.get_account_tags()
+ assert len(res.account_tags) == 1
+ assert res.account_tags[0].tag == 'tagB'
+ assert res.account_tags[0].label == 'tag B'
+ assert res.account_tags[0].accounts == [0, 1]
+ res = wallet.get_accounts('tagB')
+ assert len(res.subaddress_accounts) == 2
+ subaddress_accounts = []
+ for x in res.subaddress_accounts:
+ assert x.balance == 0
+ assert x.unlocked_balance == 0
+ subaddress_accounts.append((x.account_index, x.base_address, x.label))
+ assert sorted(subaddress_accounts) == [(0, '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'main'), (1, '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf', 'idx1_new')]
+
+ def attributes(self):
+ print('Testing attributes')
+ wallet = Wallet()
+
+ ok = False
+ try: res = wallet.get_attribute('foo')
+ except: ok = True
+ assert ok
+ res = wallet.set_attribute('foo', 'bar')
+ res = wallet.get_attribute('foo')
+ assert res.value == 'bar'
+ res = wallet.set_attribute('foo', 'いっしゅん')
+ res = wallet.get_attribute('foo')
+ assert res.value == u'いっしゅん'
+ ok = False
+ try: res = wallet.get_attribute('いちりゅう')
+ except: ok = True
+ assert ok
+ res = wallet.set_attribute('いちりゅう', 'いっぽう')
+ res = wallet.get_attribute('いちりゅう')
+ assert res.value == u'いっぽう'
+
def open_close(self):
print('Testing open/close')
wallet = Wallet()
@@ -200,11 +320,72 @@ class WalletAddressTest():
languages = res.languages
languages_local = res.languages_local
for language in languages + languages_local:
- print('Creating ' + language.encode('utf8') + ' wallet')
+ sys.stdout.write('Creating ' + language + ' wallet\n')
wallet.create_wallet(filename = '', language = language)
res = wallet.query_key('mnemonic')
wallet.close_wallet()
+ def change_password(self):
+ print('Testing password change')
+ wallet = Wallet()
+
+ # close the wallet if any, will throw if none is loaded
+ try: wallet.close_wallet()
+ except: pass
+
+ self.remove_wallet_files('test1')
+
+ 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'
+ res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.seed == seed
+
+ wallet.close_wallet()
+ res = wallet.open_wallet('test1', password = '')
+ res = wallet.get_address()
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+
+ res = wallet.change_wallet_password(old_password = '', new_password = 'foo')
+ wallet.close_wallet()
+
+ ok = False
+ try: res = wallet.open_wallet('test1', password = '')
+ except: ok = True
+ assert ok
+
+ res = wallet.open_wallet('test1', password = 'foo')
+ res = wallet.get_address()
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+
+ wallet.close_wallet()
+
+ self.remove_wallet_files('test1')
+
+ def store(self):
+ print('Testing store')
+ wallet = Wallet()
+
+ # close the wallet if any, will throw if none is loaded
+ try: wallet.close_wallet()
+ except: pass
+
+ self.remove_wallet_files('test1')
+
+ 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'
+ res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.seed == seed
+
+ self.remove_file('test1')
+ assert self.file_exists('test1.keys')
+ assert not self.file_exists('test1')
+ wallet.store()
+ assert self.file_exists('test1.keys')
+ assert self.file_exists('test1')
+
+ wallet.close_wallet()
+ self.remove_wallet_files('test1')
+
if __name__ == '__main__':
- WalletAddressTest().run_test()
+ WalletTest().run_test()
diff --git a/tests/performance_tests/rct_mlsag.h b/tests/performance_tests/rct_mlsag.h
index 59eae074e..da0c064fd 100644
--- a/tests/performance_tests/rct_mlsag.h
+++ b/tests/performance_tests/rct_mlsag.h
@@ -82,6 +82,6 @@ public:
private:
rct::keyV sk;
rct::keyM P;
- size_t ind;
+ size_t ind{};
rct::mgSig IIccss;
};
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index cac1fa943..96825f54f 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -30,7 +30,6 @@ set(unit_tests_sources
account.cpp
apply_permutation.cpp
address_from_url.cpp
- ban.cpp
base58.cpp
blockchain_db.cpp
block_queue.cpp
@@ -68,6 +67,7 @@ set(unit_tests_sources
multiexp.cpp
multisig.cpp
net.cpp
+ node_server.cpp
notify.cpp
output_distribution.cpp
parse_amount.cpp
diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp
index d7c60cecb..6b569d113 100644
--- a/tests/unit_tests/blockchain_db.cpp
+++ b/tests/unit_tests/blockchain_db.cpp
@@ -38,9 +38,6 @@
#include "string_tools.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
-#ifdef BERKELEY_DB
-#include "blockchain_db/berkeleydb/db_bdb.h"
-#endif
#include "cryptonote_basic/cryptonote_format_utils.h"
using namespace cryptonote;
@@ -233,11 +230,7 @@ protected:
using testing::Types;
-typedef Types<BlockchainLMDB
-#ifdef BERKELEY_DB
- , BlockchainBDB
-#endif
-> implementations;
+typedef Types<BlockchainLMDB> implementations;
TYPED_TEST_CASE(BlockchainDBTest, implementations);
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index 3188167f9..e5ca4e41e 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -38,6 +38,7 @@
#include "byte_slice.h"
#include "crypto/crypto.h"
#include "cryptonote_basic/connection_context.h"
+#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_protocol/levin_notify.h"
#include "int-util.h"
@@ -119,12 +120,13 @@ namespace
epee::levin::async_protocol_handler<cryptonote::levin::detail::p2p_context> handler_;
public:
- test_connection(boost::asio::io_service& io_service, cryptonote::levin::connections& connections, boost::uuids::random_generator& random_generator)
- : context_(),
- endpoint_(io_service),
+ test_connection(boost::asio::io_service& io_service, cryptonote::levin::connections& connections, boost::uuids::random_generator& random_generator, const bool is_incoming)
+ : endpoint_(io_service),
+ context_(),
handler_(std::addressof(endpoint_), connections, context_)
{
- const_cast<boost::uuids::uuid&>(context_.m_connection_id) = random_generator();
+ using base_type = epee::net_utils::connection_context_base;
+ static_cast<base_type&>(context_) = base_type{random_generator(), {}, is_incoming, false};
handler_.after_init_connection();
}
@@ -262,19 +264,19 @@ namespace
EXPECT_EQ(0u, receiver_.notified_size());
}
- void add_connection()
+ void add_connection(const bool is_incoming)
{
- contexts_.emplace_back(io_service_, *connections_, random_generator_);
+ contexts_.emplace_back(io_service_, *connections_, random_generator_, is_incoming);
EXPECT_TRUE(connection_ids_.emplace(contexts_.back().get_id()).second);
EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count());
}
- cryptonote::levin::notify make_notifier(const std::size_t noise_size)
+ cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public)
{
epee::byte_slice noise = nullptr;
if (noise_size)
noise = epee::levin::make_noise_notify(noise_size);
- return cryptonote::levin::notify{io_service_, connections_, std::move(noise)};
+ return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public};
}
boost::uuids::random_generator random_generator_;
@@ -437,10 +439,10 @@ TEST_F(levin_notify, defaulted)
TEST_F(levin_notify, flood)
{
- cryptonote::levin::notify notifier = make_notifier(0);
+ cryptonote::levin::notify notifier = make_notifier(0, true);
for (unsigned count = 0; count < 10; ++count)
- add_connection();
+ add_connection(count % 2 == 0);
{
const auto status = notifier.get_status();
@@ -500,16 +502,87 @@ TEST_F(levin_notify, flood)
}
}
+TEST_F(levin_notify, private_flood)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, false);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled); // not tracked
+ }
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
+
+ ASSERT_EQ(10u, contexts_.size());
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
+ EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
+ }
+
+ ASSERT_EQ(5u, receiver_.notified_size());
+ for (unsigned count = 0; count < 5; ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ EXPECT_EQ(txs, notification.txs);
+ EXPECT_TRUE(notification._.empty());
+ }
+ }
+
+ ASSERT_EQ(10u, contexts_.size());
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
+ EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
+ }
+
+ ASSERT_EQ(5u, receiver_.notified_size());
+ for (unsigned count = 0; count < 5; ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ EXPECT_EQ(txs, notification.txs);
+ EXPECT_FALSE(notification._.empty());
+ }
+ }
+}
+
TEST_F(levin_notify, noise)
{
for (unsigned count = 0; count < 10; ++count)
- add_connection();
+ add_connection(count % 2 == 0);
std::vector<cryptonote::blobdata> txs(1);
txs[0].resize(1900, 'h');
const boost::uuids::uuid incoming_id = random_generator_();
- cryptonote::levin::notify notifier = make_notifier(2048);
+ cryptonote::levin::notify notifier = make_notifier(2048, false);
{
const auto status = notifier.get_status();
diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp
index 056eae604..c8526abae 100644
--- a/tests/unit_tests/logging.cpp
+++ b/tests/unit_tests/logging.cpp
@@ -178,3 +178,20 @@ TEST(logging, last_precedence)
cleanup();
}
+TEST(logging, multiline)
+{
+ init();
+ mlog_set_categories("global:INFO");
+ MGINFO("first\nsecond\nthird");
+ std::string str;
+ ASSERT_TRUE(load_log_to_string(log_filename, str));
+ ASSERT_TRUE(nlines(str) == 3);
+ ASSERT_TRUE(str.find("global") != std::string::npos);
+ ASSERT_TRUE(str.find("first") != std::string::npos);
+ ASSERT_TRUE(str.find("second") != std::string::npos);
+ ASSERT_TRUE(str.find("third") != std::string::npos);
+ ASSERT_TRUE(str.find("first\nsecond") == std::string::npos);
+ ASSERT_TRUE(str.find("second\nthird") == std::string::npos);
+ cleanup();
+}
+
diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp
index d8d79a7a2..f12dd6b49 100644
--- a/tests/unit_tests/multiexp.cpp
+++ b/tests/unit_tests/multiexp.cpp
@@ -32,10 +32,10 @@
#include "ringct/rctOps.h"
#include "ringct/multiexp.h"
-static const rct::key TESTSCALAR = rct::skGen();
-static const rct::key TESTPOW2SCALAR = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
-static const rct::key TESTSMALLSCALAR = {{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
-static const rct::key TESTPOINT = rct::scalarmultBase(rct::skGen());
+#define TESTSCALAR []{ static const rct::key TESTSCALAR = rct::skGen(); return TESTSCALAR; }()
+#define TESTPOW2SCALAR []{ static const rct::key TESTPOW2SCALAR = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; return TESTPOW2SCALAR; }()
+#define TESTSMALLSCALAR []{ static const rct::key TESTSMALLSCALAR = {{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; return TESTSMALLSCALAR; }()
+#define TESTPOINT []{ static const rct::key TESTPOINT = rct::scalarmultBase(rct::skGen()); return TESTPOINT; }()
static rct::key basic(const std::vector<rct::MultiexpData> &data)
{
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 253280d4d..262541bd2 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -893,7 +893,8 @@ TEST(dandelionpp_map, empty)
TEST(dandelionpp_map, zero_stems)
{
std::vector<boost::uuids::uuid> connections{6};
- std::generate(connections.begin(), connections.end(), boost::uuids::random_generator{});
+ for (auto &c: connections)
+ c = boost::uuids::random_generator{}();
net::dandelionpp::connection_map mapper{connections, 0};
EXPECT_EQ(mapper.begin(), mapper.end());
@@ -917,7 +918,8 @@ TEST(dandelionpp_map, zero_stems)
TEST(dandelionpp_map, dropped_connection)
{
std::vector<boost::uuids::uuid> connections{6};
- std::generate(connections.begin(), connections.end(), boost::uuids::random_generator{});
+ for (auto &c: connections)
+ c = boost::uuids::random_generator{}();
std::sort(connections.begin(), connections.end());
// select 3 of 6 outgoing connections
@@ -953,7 +955,8 @@ TEST(dandelionpp_map, dropped_connection)
}
std::map<boost::uuids::uuid, boost::uuids::uuid> mapping;
std::vector<boost::uuids::uuid> in_connections{9};
- std::generate(in_connections.begin(), in_connections.end(), boost::uuids::random_generator{});
+ for (auto &c: in_connections)
+ c = boost::uuids::random_generator{}();
{
std::map<boost::uuids::uuid, std::size_t> used;
std::multimap<boost::uuids::uuid, boost::uuids::uuid> inverse_mapping;
diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/node_server.cpp
index 17fba90c6..2c89323c7 100644
--- a/tests/unit_tests/ban.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -36,6 +36,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler.inl"
#define MAKE_IPV4_ADDRESS(a,b,c,d) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),0}
+#define MAKE_IPV4_ADDRESS_PORT(a,b,c,d,e) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),e}
#define MAKE_IPV4_SUBNET(a,b,c,d,e) epee::net_utils::ipv4_network_subnet{MAKE_IP(a,b,c,d),e}
namespace cryptonote {
@@ -55,13 +56,13 @@ public:
bool get_stat_info(cryptonote::core_stat_info& st_inf) const {return true;}
bool have_block(const crypto::hash& id) const {return true;}
void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;}
- bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; }
- bool handle_incoming_txs(const std::vector<cryptonote::blobdata>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; }
+ bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; }
+ bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; }
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; }
void pause_mine(){}
void resume_mine(){}
bool on_idle(){return true;}
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;}
cryptonote::blockchain_storage &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class test_core."); }
bool get_test_drop_download() const {return true;}
@@ -83,10 +84,12 @@ public:
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; }
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
bool fluffy_blocks_enabled() const { return false; }
- uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; }
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { return 0; }
bool pad_transactions() { return false; }
uint32_t get_blockchain_pruning_seed() const { return 0; }
bool prune_blockchain(uint32_t pruning_seed = 0) { return true; }
+ bool is_within_compiled_block_hash_area(uint64_t height) const { return false; }
+ bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
void stop() {}
};
@@ -94,10 +97,10 @@ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_cor
static bool is_blocked(Server &server, const epee::net_utils::network_address &address, time_t *t = NULL)
{
- std::map<epee::net_utils::network_address, time_t> hosts = server.get_blocked_hosts();
+ std::map<std::string, time_t> hosts = server.get_blocked_hosts();
for (auto rec: hosts)
{
- if (rec.first == address)
+ if (rec.first == address.host_str())
{
if (t)
*t = rec.second;
@@ -240,5 +243,59 @@ TEST(ban, subnet)
ASSERT_TRUE(server.get_blocked_subnets().size() == 0);
}
+TEST(ban, ignores_port)
+{
+ time_t seconds;
+ test_core pr_core;
+ cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL);
+ Server server(cprotocol);
+ cprotocol.set_p2p_endpoint(&server);
+
+ ASSERT_FALSE(is_blocked(server,MAKE_IPV4_ADDRESS_PORT(1,2,3,4,5)));
+ ASSERT_TRUE(server.block_host(MAKE_IPV4_ADDRESS_PORT(1,2,3,4,5), std::numeric_limits<time_t>::max() - 1));
+ ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS_PORT(1,2,3,4,5)));
+ ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS_PORT(1,2,3,4,6)));
+ ASSERT_TRUE(server.unblock_host(MAKE_IPV4_ADDRESS_PORT(1,2,3,4,5)));
+ ASSERT_FALSE(is_blocked(server,MAKE_IPV4_ADDRESS_PORT(1,2,3,4,5)));
+ ASSERT_FALSE(is_blocked(server,MAKE_IPV4_ADDRESS_PORT(1,2,3,4,6)));
+}
+
+TEST(node_server, bind_same_p2p_port)
+{
+ const auto new_node = []() -> std::unique_ptr<Server> {
+ test_core pr_core;
+ cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL);
+ std::unique_ptr<Server> server(new Server(cprotocol));
+ cprotocol.set_p2p_endpoint(server.get());
+
+ return server;
+ };
+
+ const auto init = [](const std::unique_ptr<Server>& server, const char* port) -> bool {
+ boost::program_options::options_description desc_options("Command line options");
+ cryptonote::core::init_options(desc_options);
+ Server::init_options(desc_options);
+
+ const char *argv[2] = {nullptr, nullptr};
+ boost::program_options::variables_map vm;
+ boost::program_options::store(boost::program_options::parse_command_line(1, argv, desc_options), vm);
+
+ vm.find(nodetool::arg_p2p_bind_port.name)->second = boost::program_options::variable_value(std::string(port), false);
+
+ boost::program_options::notify(vm);
+
+ return server->init(vm);
+ };
+
+ constexpr char port[] = "48080";
+ constexpr char port_another[] = "58080";
+
+ const auto node = new_node();
+ EXPECT_TRUE(init(node, port));
+
+ EXPECT_FALSE(init(new_node(), port));
+ EXPECT_TRUE(init(new_node(), port_another));
+}
+
namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; }
namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; }
diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp
index ab634ea82..0838d2bd9 100644
--- a/tests/unit_tests/ringdb.cpp
+++ b/tests/unit_tests/ringdb.cpp
@@ -65,11 +65,17 @@ static std::pair<uint64_t, uint64_t> generate_output()
}
-static const crypto::chacha_key KEY_1 = generate_chacha_key();
-static const crypto::chacha_key KEY_2 = generate_chacha_key();
-static const crypto::key_image KEY_IMAGE_1 = generate_key_image();
-static const std::pair<uint64_t, uint64_t> OUTPUT_1 = generate_output();
-static const std::pair<uint64_t, uint64_t> OUTPUT_2 = generate_output();
+static crypto::chacha_key get_KEY_1() { static const crypto::chacha_key KEY_1 = generate_chacha_key(); return KEY_1; }
+static crypto::chacha_key get_KEY_2() { static const crypto::chacha_key KEY_2 = generate_chacha_key(); return KEY_2; }
+static crypto::key_image get_KEY_IMAGE_1() { static const crypto::key_image KEY_IMAGE_1 = generate_key_image(); return KEY_IMAGE_1; }
+static std::pair<uint64_t, uint64_t> get_OUTPUT_1() { static const std::pair<uint64_t, uint64_t> OUTPUT_1 = generate_output(); return OUTPUT_1; }
+static std::pair<uint64_t, uint64_t> get_OUTPUT_2() { static const std::pair<uint64_t, uint64_t> OUTPUT_2 = generate_output(); return OUTPUT_2; }
+
+#define KEY_1 get_KEY_1()
+#define KEY_2 get_KEY_2()
+#define KEY_IMAGE_1 get_KEY_IMAGE_1()
+#define OUTPUT_1 get_OUTPUT_1()
+#define OUTPUT_2 get_OUTPUT_2()
class RingDB: public tools::ringdb
{
diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp
index 0ae2e9c68..59e46e332 100644
--- a/tests/unit_tests/test_protocol_pack.cpp
+++ b/tests/unit_tests/test_protocol_pack.cpp
@@ -42,7 +42,7 @@ TEST(protocol_pack, protocol_pack_command)
r.total_height = 3;
for(int i = 1; i < 10000; i += i*10)
{
- r.m_block_ids.resize(i, boost::value_initialized<crypto::hash>());
+ r.m_block_ids.resize(i, crypto::hash{});
bool res = epee::serialization::store_t_to_binary(r, buff);
ASSERT_TRUE(res);