aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING31
-rw-r--r--README.md4
-rw-r--r--contrib/rlwrap/monerocommands_bitmonerod.txt1
-rw-r--r--src/common/perf_timer.h2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp14
-rw-r--r--src/cryptonote_core/cryptonote_core.h5
-rw-r--r--src/cryptonote_core/tx_pool.cpp5
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl3
-rw-r--r--src/daemon/command_parser_executor.cpp7
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp5
-rw-r--r--src/daemon/rpc_command_executor.cpp58
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h4
-rw-r--r--src/simplewallet/simplewallet.cpp299
-rw-r--r--src/wallet/api/wallet.cpp20
-rw-r--r--src/wallet/wallet2.cpp7
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--tests/core_proxy/core_proxy.h1
-rw-r--r--tests/unit_tests/ban.cpp1
-rw-r--r--utils/gpg_keys/guzzi.asc30
21 files changed, 246 insertions, 257 deletions
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 000000000..30eb221aa
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,31 @@
+Patches are preferably to be sent via a github pull request. If that
+can't be done, patches in "git format-patch" format can be sent
+(eg, posted to fpaste.org with a long enough timeout and a link
+posted to #monero-dev on irc.freenode.net).
+
+Patches should be self contained. A good rule of thumb is to have
+one patch per separate issue, feature, or logical change. Also, no
+other changes, such as random whitespace changes or reindentation.
+Following the code style of the particular chunk of code you're
+modifying is encourgaged. Proper squashing should be done (eg, if
+you're making a buggy patch, then a later patch to fix the bug,
+both patches should be merged).
+
+Commit messages should be sensible. That means a subject line that
+describes the patch, with an optional longer body that gives details,
+documentation, etc.
+
+Comments are encouraged.
+
+If modifying code for which Doxygen headers exist, that header must
+be modified to match.
+
+When submitting a pull request on github, make sure your branch is
+rebased. No merge commits nor stray commits from other people in
+your submitted branch, please. You may be asked to rebase if there
+are conflicts (even trivially resolvable ones).
+
+PGP signing commits is strongly encouraged. That should explain why
+the previous paragraph is here.
+
+Tests would be nice to have if you're adding functionality.
diff --git a/README.md b/README.md
index e17a2fa93..2245e6920 100644
--- a/README.md
+++ b/README.md
@@ -306,3 +306,7 @@ While monerod and monero-wallet-cli do not use readline directly, most of the fu
Note: rlwrap will save things like your seed and private keys, if you supply them on prompt. You may want to not use rlwrap when you use simplewallet to restore from seed, etc.
+# Contributing
+
+If you want to help out, see CONTRIBUTING for a set of guidelines.
+
diff --git a/contrib/rlwrap/monerocommands_bitmonerod.txt b/contrib/rlwrap/monerocommands_bitmonerod.txt
index 1c97cfedf..9572e40fe 100644
--- a/contrib/rlwrap/monerocommands_bitmonerod.txt
+++ b/contrib/rlwrap/monerocommands_bitmonerod.txt
@@ -19,6 +19,7 @@ print_height
print_pl
print_pool
print_pool_sh
+print_pool_stats
print_status
print_tx
q
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index 3d732012d..5eb5aaaec 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -68,7 +68,7 @@ public:
performance_timers->pop_back();
ticks = epee::misc_utils::get_tick_count() - ticks;
char s[12];
- snprintf(s, sizeof(s), "%8lu ", ticks);
+ snprintf(s, sizeof(s), "%8llu ", (unsigned long long)ticks);
LOG_PRINT("PERF " << s << std::string(performance_timers->size() * 2, ' ') << " " << name, level);
if (performance_timers->empty())
{
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index a82d96416..95c49f627 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -709,6 +709,20 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob)
+ {
+ std::list<std::pair<crypto::hash, cryptonote::transaction>> txs;
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
+ {
+ LOG_ERROR("Failed to parse relayed tranasction");
+ return;
+ }
+ txs.push_back(std::make_pair(tx_hash, std::move(tx)));
+ m_mempool.set_relayed(txs);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce)
{
return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index d925a184d..a5a97a5ad 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -180,6 +180,11 @@ namespace cryptonote
*/
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce);
+ /**
+ * @brief called when a transaction is relayed
+ */
+ virtual void on_transaction_relayed(const cryptonote::blobdata& tx);
+
/**
* @brief gets the miner instance
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 178cbda3c..dba05a539 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -387,7 +387,10 @@ namespace cryptonote
{
auto i = m_transactions.find(it->first);
if (i != m_transactions.end())
+ {
+ i->second.relayed = true;
i->second.last_relayed_time = now;
+ }
}
}
//---------------------------------------------------------------------------------
@@ -422,6 +425,8 @@ namespace cryptonote
txi.last_failed_height = txd.last_failed_height;
txi.last_failed_id_hash = epee::string_tools::pod_to_hex(txd.last_failed_id);
txi.receive_time = txd.receive_time;
+ txi.relayed = txd.relayed;
+ txi.last_relayed_time = txd.last_relayed_time;
tx_infos.push_back(txi);
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 1d1cd3631..5232d550a 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -780,6 +780,9 @@ namespace cryptonote
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)
{
+ // no check for success, so tell core they're relayed unconditionally
+ for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end(); ++tx_blob_it)
+ m_core.on_transaction_relayed(*tx_blob_it);
return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context);
}
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 5d7ed6cc0..88b77f76d 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -222,6 +222,13 @@ bool t_command_parser_executor::print_transaction_pool_short(const std::vector<s
return m_executor.print_transaction_pool_short();
}
+bool t_command_parser_executor::print_transaction_pool_stats(const std::vector<std::string>& args)
+{
+ if (!args.empty()) return false;
+
+ return m_executor.print_transaction_pool_stats();
+}
+
bool t_command_parser_executor::start_mining(const std::vector<std::string>& args)
{
if(!args.size())
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 6a984aa71..93b1fab56 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -84,6 +84,8 @@ public:
bool print_transaction_pool_short(const std::vector<std::string>& args);
+ bool print_transaction_pool_stats(const std::vector<std::string>& args);
+
bool start_mining(const std::vector<std::string>& args);
bool stop_mining(const std::vector<std::string>& args);
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 1418b920f..729b19cbf 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -111,6 +111,11 @@ t_command_server::t_command_server(
, "Print transaction pool (short format)"
);
m_command_lookup.set_handler(
+ "print_pool_stats"
+ , std::bind(&t_command_parser_executor::print_transaction_pool_stats, &m_parser, p::_1)
+ , "Print transaction pool statistics"
+ );
+ m_command_lookup.set_handler(
"show_hr"
, std::bind(&t_command_parser_executor::show_hash_rate, &m_parser, p::_1)
, "Start showing hash rate"
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index bed10715b..d8d8aac99 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -734,6 +734,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
<< "blob_size: " << tx_info.blob_size << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
+ << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl
<< "max_used_block_height: " << tx_info.max_used_block_height << std::endl
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
@@ -813,6 +814,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
<< "blob_size: " << tx_info.blob_size << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
+ << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl
<< "max_used_block_height: " << tx_info.max_used_block_height << std::endl
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
@@ -824,6 +826,62 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
return true;
}
+bool t_rpc_command_executor::print_transaction_pool_stats() {
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res;
+
+ std::string fail_message = "Problem fetching transaction pool";
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->rpc_request(req, res, "/get_transaction_pool", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << fail_message.c_str();
+ return true;
+ }
+ }
+
+ size_t n_transactions = res.transactions.size();
+ size_t bytes = 0, min_bytes = 0, max_bytes = 0;
+ size_t n_not_relayed = 0;
+ uint64_t fee = 0;
+ uint64_t oldest = 0;
+ size_t n_10m = 0;
+ size_t n_failing = 0;
+ const uint64_t now = time(NULL);
+ for (const auto &tx_info: res.transactions)
+ {
+ bytes += tx_info.blob_size;
+ if (min_bytes == 0 || bytes < min_bytes)
+ min_bytes = bytes;
+ if (bytes > max_bytes)
+ max_bytes = bytes;
+ if (!tx_info.relayed)
+ n_not_relayed++;
+ fee += tx_info.fee;
+ if (oldest == 0 || tx_info.receive_time < oldest)
+ oldest = tx_info.receive_time;
+ if (tx_info.receive_time < now - 600)
+ n_10m++;
+ if (tx_info.last_failed_height)
+ ++n_failing;
+ }
+ size_t avg_bytes = n_transactions ? bytes / n_transactions : 0;
+
+ tools::msg_writer() << n_transactions << " tx(es), " << bytes << " bytes total (min " << min_bytes << ", max " << max_bytes << ", avg " << avg_bytes << ")" << std::endl
+ << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx)" << std::endl
+ << n_not_relayed << " not relayed, " << n_failing << " failing, " << n_10m << " older than 10 minutes (oldest " << (oldest == 0 ? "-" : get_human_time_ago(oldest, now)) << ")" << std::endl;
+
+ return true;
+}
+
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index c097453e7..c1c99155b 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -96,6 +96,8 @@ public:
bool print_transaction_pool_short();
+ bool print_transaction_pool_stats();
+
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet);
bool stop_mining();
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 64f7ebf5e..85895a71a 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -857,6 +857,8 @@ namespace cryptonote
uint64_t last_failed_height;
std::string last_failed_id_hash;
uint64_t receive_time;
+ bool relayed;
+ uint64_t last_relayed_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(id_hash)
@@ -869,6 +871,8 @@ namespace cryptonote
KV_SERIALIZE(last_failed_height)
KV_SERIALIZE(last_failed_id_hash)
KV_SERIALIZE(receive_time)
+ KV_SERIALIZE(relayed)
+ KV_SERIALIZE(last_failed_id_hash)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index baa70bc0b..3f494f512 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -105,6 +105,7 @@ typedef cryptonote::simple_wallet sw;
enum TransferType {
TransferOriginal,
TransferNew,
+ TransferLocked,
};
namespace
@@ -2364,7 +2365,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
- if(local_args.size() < 2)
+ const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2;
+ if(local_args.size() < min_args)
{
fail_msg_writer() << tr("wrong number of arguments");
return true;
@@ -2372,7 +2374,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
std::vector<uint8_t> extra;
bool payment_id_seen = false;
- if (1 == local_args.size() % 2)
+ bool expect_even = (transfer_type == TransferLocked);
+ if ((expect_even ? 0 : 1) == local_args.size() % 2)
{
std::string payment_id_str = local_args.back();
local_args.pop_back();
@@ -2405,6 +2408,26 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
payment_id_seen = true;
}
+ uint64_t locked_blocks = 0;
+ if (transfer_type == TransferLocked)
+ {
+ try
+ {
+ locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back();
+ return true;
+ }
+ if (locked_blocks > 1000000)
+ {
+ fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
+ return true;
+ }
+ local_args.pop_back();
+ }
+
vector<cryptonote::tx_destination_entry> dsts;
for (size_t i = 0; i < local_args.size(); i += 2)
{
@@ -2464,8 +2487,20 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
{
// figure out what tx will be necessary
std::vector<tools::wallet2::pending_tx> ptx_vector;
+ uint64_t bc_height, unlock_block = 0;
+ std::string err;
switch (transfer_type)
{
+ case TransferLocked:
+ bc_height = get_daemon_blockchain_height(err);
+ if (!err.empty())
+ {
+ fail_msg_writer() << tr("failed to get blockchain height: ") << err;
+ return true;
+ }
+ unlock_block = bc_height + locked_blocks;
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ break;
case TransferNew:
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
break;
@@ -2507,6 +2542,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee);
if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address"))
% print_money(dust_not_in_fee);
+ if (transfer_type == TransferLocked)
+ {
+ float days = locked_blocks / 720.0f;
+ prompt << boost::format(tr(".\nThis transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % ((unsigned long long)unlock_block) % days;
+ }
prompt << tr(".") << ENDL << tr("Is this okay? (Y/Yes/N/No)");
std::string accepted = command_line::input_line(prompt.str());
@@ -2644,262 +2684,9 @@ bool simple_wallet::transfer_new(const std::vector<std::string> &args_)
return transfer_main(TransferNew, args_);
}
//----------------------------------------------------------------------------------------------------
-
bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
{
- if (!try_connect_to_daemon())
- return true;
-
- LOCK_IDLE_SCOPE();
-
- std::vector<std::string> local_args = args_;
-
- size_t fake_outs_count;
- if(local_args.size() > 0) {
- if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0]))
- {
- fake_outs_count = m_wallet->default_mixin();
- if (fake_outs_count == 0)
- fake_outs_count = DEFAULT_MIX;
- }
- else
- {
- local_args.erase(local_args.begin());
- }
- }
-
-
-
- if(m_wallet->watch_only())
- {
- fail_msg_writer() << tr("this is a watch only wallet");
- return true;
- }
-
- int locked_blocks = 0;
- std::vector<uint8_t> extra;
- bool payment_id_seen = false;
-
- if (2 < local_args.size() && local_args.size() < 5)
- {
- if (local_args.size() == 4)
- {
- std::string payment_id_str = local_args.back();
- local_args.pop_back();
-
- crypto::hash payment_id;
- bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
- if(r)
- {
- std::string extra_nonce;
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- }
- else
- {
- crypto::hash8 payment_id8;
- r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8);
- if(r)
- {
- std::string extra_nonce;
- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- }
- }
- payment_id_seen = true;
- if(!r)
- {
- fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
- return true;
- }
-
- }
-
- locked_blocks = std::stoi( local_args.back() );
- if (locked_blocks > 1000000) {
- fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
- return true;
- }
- local_args.pop_back();
-
- }
- else
- {
- fail_msg_writer() << tr("Wrong number of arguments, use: locked_transfer [<mixin_count>] <addr> <amount> <lockblocks> [<payment_id>]");
- return true;
- }
-
- vector<cryptonote::tx_destination_entry> dsts;
-
- cryptonote::tx_destination_entry de;
- bool has_payment_id;
- crypto::hash8 new_payment_id;
- if (!get_address_from_str(local_args[0], de.addr, has_payment_id, new_payment_id))
- return true;
-
- bool ok = cryptonote::parse_amount(de.amount, local_args[1]);
- if(!ok || 0 == de.amount)
- {
- fail_msg_writer() << tr("amount is wrong: ") << local_args[0] << ' ' << local_args[1] <<
- ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max());
- return true;
- }
-
- dsts.push_back(de);
-
-
- try
- {
- // figure out what tx will be necessary
- std::vector<tools::wallet2::pending_tx> ptx_vector;
-
- std::string err;
- int bc_height = get_daemon_blockchain_height(err);
- int unlock_block = locked_blocks + bc_height;
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block , 0 /* unused fee arg*/, extra, m_trusted_daemon);
-
- uint64_t total_fee = 0;
- uint64_t dust_not_in_fee = 0;
- uint64_t dust_in_fee = 0;
- for (size_t n = 0; n < ptx_vector.size(); ++n)
- {
- total_fee += ptx_vector[n].fee;
-
- if (ptx_vector[n].dust_added_to_fee)
- dust_in_fee += ptx_vector[n].dust;
- else
- dust_not_in_fee += ptx_vector[n].dust;
- }
-
- std::stringstream prompt;
- if (ptx_vector.size() > 1)
- {
- prompt << boost::format(tr("Your transaction needs to be split into %llu transactions. "
- "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) %
- ((unsigned long long)ptx_vector.size()) % print_money(total_fee);
- }
- else
- {
-
- prompt << boost::format(tr("Transaction fee is %s")) %
- print_money(total_fee);
- }
- if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee);
- if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address"))
- % print_money(dust_not_in_fee);
-
- float days = (float)(locked_blocks) / 720.0;
- prompt << boost::format(tr(".\nThe unlock time is approximately %s days")) % days;
-
- prompt << tr(".") << ENDL << tr("Is this okay? (Y/Yes/N/No)");
-
- std::string accepted = command_line::input_line(prompt.str());
- if (std::cin.eof())
- return true;
- if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes")
- {
- fail_msg_writer() << tr("transaction cancelled.");
- return true;
- }
-
- // actually commit the transactions
- while (!ptx_vector.empty())
- {
- auto & ptx = ptx_vector.back();
- m_wallet->commit_tx(ptx);
- success_msg_writer(true) << tr("Money successfully sent, transaction ") << get_transaction_hash(ptx.tx);
-
- // if no exception, remove element from vector
- ptx_vector.pop_back();
- }
- }
- catch (const tools::error::daemon_busy&)
- {
- fail_msg_writer() << tr("daemon is busy. Please try again later.");
- }
- catch (const tools::error::no_connection_to_daemon&)
- {
- fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
- }
- catch (const tools::error::wallet_rpc_error& e)
- {
- LOG_ERROR("RPC error: " << e.to_string());
- fail_msg_writer() << tr("RPC error: ") << e.what();
- }
- catch (const tools::error::get_random_outs_error&)
- {
- fail_msg_writer() << tr("failed to get random outputs to mix");
- }
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in unlocked balance");
- }
- catch (const tools::error::tx_not_possible& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
- print_money(e.available()) %
- print_money(e.tx_amount() + e.fee()) %
- print_money(e.tx_amount()) %
- print_money(e.fee()));
- fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
- }
- catch (const tools::error::not_enough_outs_to_mix& e)
- {
- auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":";
- for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
- {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second;
- }
- }
- catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
- }
- catch (const tools::error::tx_rejected& e)
- {
- fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- std::string reason = e.reason();
- if (!reason.empty())
- fail_msg_writer() << tr("Reason: ") << reason;
- }
- catch (const tools::error::tx_sum_overflow& e)
- {
- fail_msg_writer() << e.what();
- }
- catch (const tools::error::zero_destination&)
- {
- fail_msg_writer() << tr("one of destinations is zero");
- }
- catch (const tools::error::tx_too_big& e)
- {
- fail_msg_writer() << tr("failed to find a suitable way to split transactions");
- }
- catch (const tools::error::transfer_error& e)
- {
- LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
- }
- catch (const tools::error::wallet_internal_error& e)
- {
- LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
- }
- catch (const std::exception& e)
- {
- LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
- }
- catch (...)
- {
- LOG_ERROR("unknown error");
- fail_msg_writer() << tr("unknown error");
- }
-
- return true;
+ return transfer_main(TransferLocked, args_);
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 747263b54..9a9638b40 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -167,6 +167,26 @@ bool Wallet::paymentIdValid(const string &paiment_id)
return false;
}
+bool Wallet::addressValid(const std::string &str, bool testnet)
+{
+ bool has_payment_id;
+ cryptonote::account_public_address address;
+ crypto::hash8 pid;
+ return get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, str);
+}
+
+std::string Wallet::paymentIdFromAddress(const std::string &str, bool testnet)
+{
+ bool has_payment_id;
+ cryptonote::account_public_address address;
+ crypto::hash8 pid;
+ if (!get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, str))
+ return "";
+ if (!has_payment_id)
+ return "";
+ return epee::string_tools::pod_to_hex(pid);
+}
+
uint64_t Wallet::maximumAllowedAmount()
{
return std::numeric_limits<uint64_t>::max();
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index b499f6b24..23e016f7b 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -2523,6 +2523,13 @@ void wallet2::commit_tx(pending_tx& ptx)
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status, daemon_send_resp.reason);
+ // sanity checks
+ for (size_t idx: ptx.selected_transfers)
+ {
+ THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
+ "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
+ }
+
txid = get_transaction_hash(ptx.tx);
crypto::hash payment_id = cryptonote::null_hash;
std::vector<cryptonote::tx_destination_entry> dests;
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 0f622c26c..8427ba250 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -280,6 +280,8 @@ struct Wallet
static uint64_t amountFromDouble(double amount);
static std::string genPaymentId();
static bool paymentIdValid(const std::string &paiment_id);
+ static bool addressValid(const std::string &str, bool testnet);
+ static std::string paymentIdFromAddress(const std::string &str, bool testnet);
static uint64_t maximumAllowedAmount();
/**
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h
index 995b0c650..a265ef1ce 100644
--- a/tests/core_proxy/core_proxy.h
+++ b/tests/core_proxy/core_proxy.h
@@ -88,5 +88,6 @@ namespace tests
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
uint64_t get_target_blockchain_height() const { return 1; }
size_t get_block_sync_size() const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
+ virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {}
};
}
diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp
index 85b0298b7..e82661545 100644
--- a/tests/unit_tests/ban.cpp
+++ b/tests/unit_tests/ban.cpp
@@ -63,6 +63,7 @@ public:
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
uint64_t get_target_blockchain_height() const { return 1; }
size_t get_block_sync_size() const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
+ virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {}
};
typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_core>> Server;
diff --git a/utils/gpg_keys/guzzi.asc b/utils/gpg_keys/guzzi.asc
new file mode 100644
index 000000000..2c374523f
--- /dev/null
+++ b/utils/gpg_keys/guzzi.asc
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQENBFed+r8BCADScb2cSXAk7v0+tbEUDOf6VuhKc26C2Kfwk/Ei2iWC7Vaa/bru
+aNLpM1nVuWKdNnfX3MZo38yItvxS4HlkI29pXcrzyqd3sfPklPgLliaayKkqh1Xx
+jl6T5NM9VVJ5dMo0pMzns49xFknOsBXdSH/ziZVD0SuSNctNb5XraCrfuDPw0fn0
+lLZ4a6WwUXL9+4Y+xbvNmYHjlnAB7xjvOpprSuJ769zDGpQlV2UXeRqjjMPzVbH4
+PYgmNAItbhvog2UfKeQK8K0Fwj1uNsZ5fnqvoa9lsgsbyV4x4DbQu/W5NVP1ylDC
+MnHKABa8Rg8zzjp0G7YuBGPy/JPffwZBoEuhABEBAAG0I2d1enppam9uZXMgPGd1
+enppam9uZXMxMkBnbWFpbC5jb20+iQE5BBMBCAAjBQJXnfq/AhsDBwsJCAcDAgEG
+FQgCCQoLBBYCAwECHgECF4AACgkQXNw+OlijFbIhbggAt6pEz8g++3vHXFaEsOiK
+fSJYheSuY2NGOgmS2WBWdPp6z7nobSScYzCeF4pOnCFxM99O7i9/kfDzVp4W7lXL
+VIvLiLvKwWLkVhHhgOlerLRYNR+TjS+GtGhhL6Y2Yj1AkG2pJd59SBhbhdkqdNo0
+D614GjnyK8SGlz9xjV9ZE4csTPH2p9xOqJoRCoUuEGWHNoox0vJTuJuKhCHta1y8
+T84uFcGCagxHxqv5eqgype32iueSMfsbyyFJ2WaLaCyYKcPGbXG9iFFLqtvJU9xv
+46oPYd1HYjtXVaRnbtiDljlokEiXiQ7WPsYEgZy76KMJ30fEyXICPYvTDR1aFLYI
+1LkBDQRXnfq/AQgA2JHleHFNtQM1ECeEGAYoGzt+IyPKzuT43ZgwuxK4t0kfKNN4
+KvihBcmFqjJAwwmS+9oNPeU4BgZ4k8DX2JP3JoGzFF7MK+i8OFW0ckZ4kbNZhtq3
+e8a1fkWnkA6pQA1JppiZqqI+VNLTTPvsH8pG2UA4rL4XvxeJ7jrFnQMCfiOiuIsd
+C+MeUAHthNeLreq1LXx0s0GfPMwMM5ckKdtEKmMVCw1zZ/J2RyBzkyWdyMVPSRRV
+lCDtgSivCSG+Y+ub4tgV8ast2/wKxCV92oRaeMQVsWZ9PHdVq0tUn4I98UtCwOh7
+reMPi6a8eWJcQ/s07schqXt2iTssv/1V3PTjZQARAQABiQEfBBgBCAAJBQJXnfq/
+AhsMAAoJEFzcPjpYoxWydDsH/2sLAtzKVgbeZFF/0e+6r/P0R3Fgkv6N9o+w4A5N
+pMDXCNjFjWVkYRgyON8Y8ijkkbIkBcXmp+01HxZjrQI0WavQaLj2tavz9Np/8wPb
+UXZYc0zjxki9mdFdNDW1vgoT9nSctB4bp0xf/NnYmkPMQfDzruVkf8bW6YQzkZRP
+apjY9IxUPKFx9hQcDAsov1xXww2uQPwEGHfeSMkeCU9zfPBNmaFCvBfFTFu5UHsl
+g2hw95UUsDlpYcMs7YdqYw40EXcTQJk1aoWT7kjO1KCQWF5EDc0YRhw/REXN9fJe
+S77oEy23Q/RtJQBXzHw3chyhe4/XMQQbmuY9+OfmhHVAbF4=
+=lOXz
+-----END PGP PUBLIC KEY BLOCK-----