aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--contrib/epee/include/console_handler.h2
-rw-r--r--contrib/epee/include/storages/portable_storage_from_json.h1
-rw-r--r--src/cryptonote_basic/account.h2
-rw-r--r--src/cryptonote_core/tx_pool.cpp8
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h18
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl62
-rw-r--r--src/p2p/net_node.inl18
-rw-r--r--src/simplewallet/simplewallet.cpp57
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/wallet2.cpp23
-rw-r--r--src/wallet/wallet2.h3
-rw-r--r--src/wallet/wallet_rpc_server.cpp33
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h3
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
-rwxr-xr-xtests/functional_tests/multisig.py10
-rw-r--r--tests/unit_tests/node_server.cpp4
-rw-r--r--utils/build_scripts/windows.bat45
-rw-r--r--utils/python-rpc/framework/wallet.py3
19 files changed, 198 insertions, 102 deletions
diff --git a/.gitignore b/.gitignore
index a39168ac5..9f62575e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,12 @@ external/miniupnpc/Makefile
miniupnpcstrings.h
version/
ClangBuildAnalyzerSession.txt
+
+# gitian
+contrib/gitian/builder/
+contrib/gitian/docker/
+contrib/gitian/sigs/
+
# Created by https://www.gitignore.io
### C++ ###
diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h
index 219b593b0..7115fce0c 100644
--- a/contrib/epee/include/console_handler.h
+++ b/contrib/epee/include/console_handler.h
@@ -196,7 +196,7 @@ namespace epee
if (m_read_status == state_cancelled)
return false;
- int retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100);
+ DWORD retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100);
switch (retval)
{
case WAIT_FAILED:
diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h
index 3021598f5..69192ca6b 100644
--- a/contrib/epee/include/storages/portable_storage_from_json.h
+++ b/contrib/epee/include/storages/portable_storage_from_json.h
@@ -51,7 +51,6 @@ namespace epee
{
CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded");
- std::string::const_iterator sub_element_start;
std::string name;
typename t_storage::harray h_array = nullptr;
enum match_state
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index 6e887db6d..2ee9545d4 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -55,8 +55,6 @@ namespace cryptonote
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
END_KV_SERIALIZE_MAP()
- account_keys& operator=(account_keys const&) = default;
-
void encrypt(const crypto::chacha_key &key);
void decrypt(const crypto::chacha_key &key);
void encrypt_viewkey(const crypto::chacha_key &key);
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index c27261860..a68da0e62 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -97,9 +97,9 @@ namespace cryptonote
constexpr const std::chrono::seconds forward_delay_average{CRYPTONOTE_FORWARD_DELAY_AVERAGE};
// a kind of increasing backoff within min/max bounds
- uint64_t get_relay_delay(time_t now, time_t received)
+ uint64_t get_relay_delay(time_t last_relay, time_t received)
{
- time_t d = (now - received + MIN_RELAY_TIME) / MIN_RELAY_TIME * MIN_RELAY_TIME;
+ time_t d = (last_relay - received + MIN_RELAY_TIME) / MIN_RELAY_TIME * MIN_RELAY_TIME;
if (d > MAX_RELAY_TIME)
d = MAX_RELAY_TIME;
return d;
@@ -779,7 +779,7 @@ namespace cryptonote
case relay_method::local:
case relay_method::fluff:
case relay_method::block:
- if (now - meta.last_relayed_time <= get_relay_delay(now, meta.receive_time))
+ if (now - meta.last_relayed_time <= get_relay_delay(meta.last_relayed_time, meta.receive_time))
return true; // continue to next tx
break;
}
@@ -812,7 +812,7 @@ namespace cryptonote
function is only called every ~2 minutes, so this resetting should be
unnecessary, but is primarily a precaution against potential changes
to the callback routines. */
- elem.second.last_relayed_time = now + get_relay_delay(now, elem.second.receive_time);
+ elem.second.last_relayed_time = now + get_relay_delay(elem.second.last_relayed_time, elem.second.receive_time);
m_blockchain.update_txpool_tx(elem.first, elem.second);
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index a1e4df563..515b78c94 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -113,12 +113,23 @@ namespace cryptonote
const block_queue &get_block_queue() const { return m_block_queue; }
void stop();
void on_connection_close(cryptonote_connection_context &context);
- void set_max_out_peers(unsigned int max) { m_max_out_peers = max; }
+ void set_max_out_peers(epee::net_utils::zone zone, unsigned int max) { CRITICAL_REGION_LOCAL(m_max_out_peers_lock); m_max_out_peers[zone] = max; }
+ unsigned int get_max_out_peers(epee::net_utils::zone zone) const
+ {
+ CRITICAL_REGION_LOCAL(m_max_out_peers_lock);
+ const auto it = m_max_out_peers.find(zone);
+ if (it == m_max_out_peers.end())
+ {
+ MWARNING(epee::net_utils::zone_to_string(zone) << " max out peers not set, using default");
+ return P2P_DEFAULT_CONNECTIONS_COUNT;
+ }
+ return it->second;
+ }
bool no_sync() const { return m_no_sync; }
void set_no_sync(bool value) { m_no_sync = value; }
std::string get_peers_overview() const;
std::pair<uint32_t, uint32_t> get_next_needed_pruning_stripe() const;
- bool needs_new_sync_connections() const;
+ bool needs_new_sync_connections(epee::net_utils::zone zone) const;
bool is_busy_syncing();
private:
@@ -171,7 +182,8 @@ namespace cryptonote
epee::math_helper::once_a_time_milliseconds<100> m_standby_checker;
epee::math_helper::once_a_time_seconds<101> m_sync_search_checker;
epee::math_helper::once_a_time_seconds<43> m_bad_peer_checker;
- std::atomic<unsigned int> m_max_out_peers;
+ std::unordered_map<epee::net_utils::zone, unsigned int> m_max_out_peers;
+ mutable epee::critical_section m_max_out_peers_lock;
tools::PerformanceTimer m_sync_timer, m_add_timer;
uint64_t m_last_add_end_time;
uint64_t m_sync_spans_downloaded, m_sync_old_spans_downloaded, m_sync_bad_spans_downloaded;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 891ee109d..af3031263 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -1776,33 +1776,49 @@ skip:
return true;
MTRACE("Checking for outgoing syncing peers...");
- unsigned n_syncing = 0, n_synced = 0;
- boost::uuids::uuid last_synced_peer_id(boost::uuids::nil_uuid());
+ std::unordered_map<epee::net_utils::zone, unsigned> n_syncing, n_synced;
+ std::unordered_map<epee::net_utils::zone, boost::uuids::uuid> last_synced_peer_id;
+ std::vector<epee::net_utils::zone> zones;
m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool
{
if (!peer_id || context.m_is_income) // only consider connected outgoing peers
return true;
+
+ const epee::net_utils::zone zone = context.m_remote_address.get_zone();
+ if (n_syncing.find(zone) == n_syncing.end())
+ {
+ n_syncing[zone] = 0;
+ n_synced[zone] = 0;
+ last_synced_peer_id[zone] = boost::uuids::nil_uuid();
+ zones.push_back(zone);
+ }
+
if (context.m_state == cryptonote_connection_context::state_synchronizing)
- ++n_syncing;
+ ++n_syncing[zone];
if (context.m_state == cryptonote_connection_context::state_normal)
{
- ++n_synced;
+ ++n_synced[zone];
if (!context.m_anchor)
- last_synced_peer_id = context.m_connection_id;
+ last_synced_peer_id[zone] = context.m_connection_id;
}
return true;
});
- MTRACE(n_syncing << " syncing, " << n_synced << " synced");
- // if we're at max out peers, and not enough are syncing
- if (n_synced + n_syncing >= m_max_out_peers && n_syncing < P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT && last_synced_peer_id != boost::uuids::nil_uuid())
+ for (const auto& zone : zones)
{
- if (!m_p2p->for_connection(last_synced_peer_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{
- MINFO(ctx << "dropping synced peer, " << n_syncing << " syncing, " << n_synced << " synced");
- drop_connection(ctx, false, false);
- return true;
- }))
- MDEBUG("Failed to find peer we wanted to drop");
+ const unsigned int max_out_peers = get_max_out_peers(zone);
+ MTRACE("[" << epee::net_utils::zone_to_string(zone) << "] " << n_syncing[zone] << " syncing, " << n_synced[zone] << " synced, " << max_out_peers << " max out peers");
+
+ // if we're at max out peers, and not enough are syncing, drop the last sync'd non-anchor
+ if (n_synced[zone] + n_syncing[zone] >= max_out_peers && n_syncing[zone] < P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT && last_synced_peer_id[zone] != boost::uuids::nil_uuid())
+ {
+ if (!m_p2p->for_connection(last_synced_peer_id[zone], [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{
+ MINFO(ctx << "dropping synced peer, " << n_syncing[zone] << " syncing, " << n_synced[zone] << " synced, " << max_out_peers << " max out peers");
+ drop_connection(ctx, false, false);
+ return true;
+ }))
+ MDEBUG("Failed to find peer we wanted to drop");
+ }
}
return true;
@@ -1987,11 +2003,13 @@ skip:
++n_peers_on_next_stripe;
return true;
});
+ // TODO: investigate tallying by zone and comparing to max out peers by zone
+ const unsigned int max_out_peers = get_max_out_peers(epee::net_utils::zone::public_);
const uint32_t distance = (peer_stripe + (1<<CRYPTONOTE_PRUNING_LOG_STRIPES) - next_stripe) % (1<<CRYPTONOTE_PRUNING_LOG_STRIPES);
- if ((n_out_peers >= m_max_out_peers && n_peers_on_next_stripe == 0) || (distance > 1 && n_peers_on_next_stripe <= 2) || distance > 2)
+ if ((n_out_peers >= max_out_peers && n_peers_on_next_stripe == 0) || (distance > 1 && n_peers_on_next_stripe <= 2) || distance > 2)
{
MDEBUG(context << "we want seed " << next_stripe << ", and either " << n_out_peers << " is at max out peers ("
- << m_max_out_peers << ") or distance " << distance << " from " << next_stripe << " to " << peer_stripe <<
+ << max_out_peers << ") or distance " << distance << " from " << next_stripe << " to " << peer_stripe <<
" is too large and we have only " << n_peers_on_next_stripe << " peers on next seed, dropping connection to make space");
return true;
}
@@ -2812,11 +2830,13 @@ skip:
}
return true;
});
- const bool use_next = (n_next > m_max_out_peers / 2 && n_subsequent <= 1) || (n_next > 2 && n_subsequent == 0);
+ // TODO: investigate tallying by zone and comparing to max out peers by zone
+ const unsigned int max_out_peers = get_max_out_peers(epee::net_utils::zone::public_);
+ const bool use_next = (n_next > max_out_peers / 2 && n_subsequent <= 1) || (n_next > 2 && n_subsequent == 0);
const uint32_t ret_stripe = use_next ? subsequent_pruning_stripe: next_pruning_stripe;
MIDEBUG(const std::string po = get_peers_overview(), "get_next_needed_pruning_stripe: want height " << want_height << " (" <<
want_height_from_blockchain << " from blockchain, " << want_height_from_block_queue << " from block queue), stripe " <<
- next_pruning_stripe << " (" << n_next << "/" << m_max_out_peers << " on it and " << n_subsequent << " on " <<
+ next_pruning_stripe << " (" << n_next << "/" << max_out_peers << " on it and " << n_subsequent << " on " <<
subsequent_pruning_stripe << ", " << n_others << " others) -> " << ret_stripe << " (+" <<
(ret_stripe - next_pruning_stripe + (1 << CRYPTONOTE_PRUNING_LOG_STRIPES)) % (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) <<
"), current peers " << po);
@@ -2824,7 +2844,7 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- bool t_cryptonote_protocol_handler<t_core>::needs_new_sync_connections() const
+ bool t_cryptonote_protocol_handler<t_core>::needs_new_sync_connections(epee::net_utils::zone zone) const
{
const uint64_t target = m_core.get_target_blockchain_height();
const uint64_t height = m_core.get_current_blockchain_height();
@@ -2832,11 +2852,11 @@ skip:
return false;
size_t n_out_peers = 0;
m_p2p->for_each_connection([&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{
- if (!ctx.m_is_income)
+ if (!ctx.m_is_income && ctx.m_remote_address.get_zone() == zone)
++n_out_peers;
return true;
});
- if (n_out_peers >= m_max_out_peers)
+ if (n_out_peers >= get_max_out_peers(zone))
return false;
return true;
}
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index a3bc3bf24..f33ce977d 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -538,7 +538,7 @@ namespace nodetool
if ( !set_max_out_peers(public_zone, command_line::get_arg(vm, arg_out_peers) ) )
return false;
else
- m_payload_handler.set_max_out_peers(public_zone.m_config.m_net_config.max_out_connection_count);
+ m_payload_handler.set_max_out_peers(epee::net_utils::zone::public_, public_zone.m_config.m_net_config.max_out_connection_count);
if ( !set_max_in_peers(public_zone, command_line::get_arg(vm, arg_in_peers) ) )
@@ -575,6 +575,8 @@ namespace nodetool
if (!set_max_out_peers(zone, proxy.max_connections))
return false;
+ else
+ m_payload_handler.set_max_out_peers(proxy.zone, proxy.max_connections);
epee::byte_slice this_noise = nullptr;
if (proxy.noise)
@@ -2462,8 +2464,12 @@ namespace nodetool
const epee::net_utils::zone zone_type = context.m_remote_address.get_zone();
network_zone& zone = m_network_zones.at(zone_type);
+ //will add self to peerlist if in same zone as outgoing later in this function
+ const bool outgoing_to_same_zone = !context.m_is_income && zone.m_our_address.get_zone() == zone_type;
+ const uint32_t max_peerlist_size = P2P_DEFAULT_PEERS_IN_HANDSHAKE - (outgoing_to_same_zone ? 1 : 0);
+
std::vector<peerlist_entry> local_peerlist_new;
- zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, P2P_DEFAULT_PEERS_IN_HANDSHAKE);
+ zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, max_peerlist_size);
//only include out peers we did not already send
rsp.local_peerlist_new.reserve(local_peerlist_new.size());
@@ -2483,7 +2489,7 @@ namespace nodetool
etc., because someone could give faulty addresses over Tor/I2P to get the
real peer with that identity banned/blacklisted. */
- if(!context.m_is_income && zone.m_our_address.get_zone() == zone_type)
+ if(outgoing_to_same_zone)
rsp.local_peerlist_new.push_back(peerlist_entry{zone.m_our_address, zone.m_config.m_peer_id, std::time(nullptr)});
LOG_DEBUG_CC(context, "COMMAND_TIMED_SYNC");
@@ -2758,7 +2764,7 @@ namespace nodetool
public_zone->second.m_config.m_net_config.max_out_connection_count = count;
if(current > count)
public_zone->second.m_net_server.get_config_object().del_out_connections(current - count);
- m_payload_handler.set_max_out_peers(count);
+ m_payload_handler.set_max_out_peers(epee::net_utils::zone::public_, count);
}
}
@@ -2887,10 +2893,12 @@ namespace nodetool
{
if (m_offline) return true;
if (!m_exclusive_peers.empty()) return true;
- if (m_payload_handler.needs_new_sync_connections()) return true;
for (auto& zone : m_network_zones)
{
+ if (m_payload_handler.needs_new_sync_connections(zone.first))
+ continue;
+
if (zone.second.m_net_server.is_stop_signal_sent())
return false;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index d3e40ab74..f6e313089 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -142,6 +142,19 @@ typedef cryptonote::simple_wallet sw;
#define MIN_PAYMENT_RATE 0.01f // per hash
#define MAX_MNEW_ADDRESSES 1000
+#define CHECK_MULTISIG_ENABLED() \
+ do \
+ { \
+ if (!m_wallet->is_multisig_enabled()) \
+ { \
+ fail_msg_writer() << tr("Multisig is disabled."); \
+ fail_msg_writer() << tr("Multisig is an experimental feature and may have bugs. Things that could go wrong include: funds sent to a multisig wallet can't be spent at all, can only be spent with the participation of a malicious group member, or can be stolen by a malicious group member."); \
+ fail_msg_writer() << tr("You can enable it with:"); \
+ fail_msg_writer() << tr(" set enable-multisig-experimental 1"); \
+ return false; \
+ } \
+ } while(0)
+
enum TransferType {
Transfer,
TransferLocked,
@@ -986,12 +999,14 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std:
bool simple_wallet::prepare_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
prepare_multisig_main(args, false);
return true;
}
bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
@@ -1031,12 +1046,14 @@ bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args,
bool simple_wallet::make_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
make_multisig_main(args, false);
return true;
}
bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
@@ -1121,11 +1138,13 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo
bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
exchange_multisig_keys_main(args, false);
return true;
}
bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms) {
+ CHECK_MULTISIG_ENABLED();
bool ready;
if (m_wallet->key_on_device())
{
@@ -1189,12 +1208,14 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &
bool simple_wallet::export_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
export_multisig_main(args, false);
return true;
}
bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
bool ready;
if (m_wallet->key_on_device())
{
@@ -1254,12 +1275,14 @@ bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, b
bool simple_wallet::import_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
import_multisig_main(args, false);
return true;
}
bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
bool ready;
uint32_t threshold, total;
if (m_wallet->key_on_device())
@@ -1349,12 +1372,14 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs)
bool simple_wallet::sign_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
sign_multisig_main(args, false);
return true;
}
bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
bool ready;
if (m_wallet->key_on_device())
{
@@ -1464,12 +1489,14 @@ bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, boo
bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
submit_multisig_main(args, false);
return true;
}
bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
{
+ CHECK_MULTISIG_ENABLED();
bool ready;
uint32_t threshold;
if (m_wallet->key_on_device())
@@ -1551,6 +1578,7 @@ bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, b
bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
bool ready;
uint32_t threshold;
if (m_wallet->key_on_device())
@@ -3074,6 +3102,25 @@ bool simple_wallet::set_load_deprecated_formats(const std::vector<std::string> &
return true;
}
+bool simple_wallet::set_enable_multisig(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ if (args.size() < 2)
+ {
+ fail_msg_writer() << tr("Value not specified");
+ return true;
+ }
+
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->enable_multisig(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
+ }
+ return true;
+}
+
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if(args.empty())
@@ -3391,6 +3438,8 @@ simple_wallet::simple_wallet()
" The RPC payment credits balance to target (0 for default).\n "
"show-wallet-name-when-locked <1|0>\n "
" Set this if you would like to display the wallet name when locked.\n "
+ "enable-multisig-experimental <1|0>\n "
+ " Set this to allow multisig commands. Multisig may currently be exploitable if parties do not trust each other.\n "
"inactivity-lock-timeout <unsigned int>\n "
" How many seconds to wait before locking the wallet (0 to disable)."));
m_cmd_binder.set_handler("encrypted_seed",
@@ -3806,6 +3855,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "auto-mine-for-rpc-payment-threshold = " << m_wallet->auto_mine_for_rpc_payment_threshold();
success_msg_writer() << "credits-target = " << m_wallet->credits_target();
success_msg_writer() << "load-deprecated-formats = " << m_wallet->load_deprecated_formats();
+ success_msg_writer() << "enable-multisig-experimental = " << m_wallet->is_multisig_enabled();
return true;
}
else
@@ -3872,6 +3922,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("persistent-rpc-client-id", set_persistent_rpc_client_id, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("auto-mine-for-rpc-payment-threshold", set_auto_mine_for_rpc_payment_threshold, tr("floating point >= 0"));
CHECK_SIMPLE_VARIABLE("credits-target", set_credits_target, tr("unsigned integer"));
+ CHECK_SIMPLE_VARIABLE("enable-multisig-experimental", set_enable_multisig, tr("0 or 1"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@@ -6560,7 +6611,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
vector<cryptonote::address_parse_info> dsts_info;
vector<cryptonote::tx_destination_entry> dsts;
- size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); )
{
dsts_info.emplace_back();
@@ -6619,7 +6669,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
de.addr = info.address;
de.is_subaddress = info.is_subaddress;
de.is_integrated = info.has_payment_id;
- num_subaddresses += info.is_subaddress;
if (info.has_payment_id || !payment_id_uri.empty())
{
@@ -6980,6 +7029,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
// actually commit the transactions
if (m_wallet->multisig())
{
+ CHECK_MULTISIG_ENABLED();
bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx");
if (!r)
{
@@ -7284,6 +7334,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
// actually commit the transactions
if (m_wallet->multisig())
{
+ CHECK_MULTISIG_ENABLED();
bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx");
if (!r)
{
@@ -7518,6 +7569,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
// actually commit the transactions
if (m_wallet->multisig())
{
+ CHECK_MULTISIG_ENABLED();
bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx");
if (!r)
{
@@ -11549,6 +11601,7 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
bool simple_wallet::mms(const std::vector<std::string> &args)
{
+ CHECK_MULTISIG_ENABLED();
try
{
m_wallet->get_multisig_wallet_state();
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 4c005c53a..6c4ddd4e7 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -153,6 +153,7 @@ namespace cryptonote
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>());
bool set_load_deprecated_formats(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_enable_multisig(const std::vector<std::string> &args = std::vector<std::string>());
bool set_persistent_rpc_client_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args = std::vector<std::string>());
bool set_credits_target(const std::vector<std::string> &args = std::vector<std::string>());
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 1eeb893c0..0b2a6c0f5 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1216,7 +1216,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_rpc_version(0),
m_export_format(ExportFormat::Binary),
m_load_deprecated_formats(false),
- m_credits_target(0)
+ m_credits_target(0),
+ m_enable_multisig(false)
{
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
}
@@ -2882,6 +2883,11 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
" (height " + std::to_string(start_height) + "), local block id at this height: " +
string_tools::pod_to_hex(m_blockchain[current_index]));
+ const uint64_t reorg_depth = m_blockchain.size() - current_index;
+ THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error,
+ tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") +
+ std::to_string(reorg_depth));
+
detach_blockchain(current_index, output_tracker_cache);
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
}
@@ -3532,15 +3538,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
first = false;
- if (!next_blocks.empty())
- {
- const uint64_t expected_start_height = std::max(static_cast<uint64_t>(m_blockchain.size()), uint64_t(1)) - 1;
- const uint64_t reorg_depth = expected_start_height - std::min(expected_start_height, next_blocks_start_height);
- THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error,
- tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") +
- std::to_string(reorg_depth));
- }
-
// if we've got at least 10 blocks to refresh, assume we're starting
// a long refresh, and setup a tracking output cache if we need to
if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10)
@@ -4051,6 +4048,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
value2.SetUint64(m_credits_target);
json.AddMember("credits_target", value2, json.GetAllocator());
+ value2.SetInt(m_enable_multisig ? 1 : 0);
+ json.AddMember("enable_multisig", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -4199,6 +4199,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_persistent_rpc_client_id = false;
m_auto_mine_for_rpc_payment_threshold = -1.0f;
m_credits_target = 0;
+ m_enable_multisig = false;
}
else if(json.IsObject())
{
@@ -4431,6 +4432,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_auto_mine_for_rpc_payment_threshold = field_auto_mine_for_rpc_payment;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, credits_target, uint64_t, Uint64, false, 0);
m_credits_target = field_credits_target;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false);
+ m_enable_multisig = field_enable_multisig;
}
else
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index e9e5bc95e..e051946ad 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1297,6 +1297,8 @@ private:
void set_rpc_client_secret_key(const crypto::secret_key &key) { m_rpc_client_secret_key = key; m_node_rpc_proxy.set_client_secret_key(key); }
uint64_t credits_target() const { return m_credits_target; }
void credits_target(uint64_t threshold) { m_credits_target = threshold; }
+ bool is_multisig_enabled() const { return m_enable_multisig; }
+ void enable_multisig(bool enable) { m_enable_multisig = enable; }
bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const boost::optional<cryptonote::account_public_address> &single_destination_subaddress = boost::none);
@@ -1813,6 +1815,7 @@ private:
crypto::secret_key m_rpc_client_secret_key;
rpc_payment_state_t m_rpc_payment_state;
uint64_t m_credits_target;
+ bool m_enable_multisig;
// Aux transaction data from device
serializable_unordered_map<crypto::hash, std::string> m_tx_device;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 57baf428f..7ec5fc7a1 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -61,6 +61,17 @@ using namespace epee;
#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
+#define CHECK_MULTISIG_ENABLED() \
+ do \
+ { \
+ if (m_wallet->multisig() && !m_wallet->is_multisig_enabled()) \
+ { \
+ er.code = WALLET_RPC_ERROR_CODE_DISABLED; \
+ er.message = "This wallet is multisig, and multisig is disabled. Multisig is an experimental feature and may have bugs. Things that could go wrong include: funds sent to a multisig wallet can't be spent at all, can only be spent with the participation of a malicious group member, or can be stolen by a malicious group member. You can enable it by running this once in monero-wallet-cli: set enable-multisig-experimental 1"; \
+ return false; \
+ } \
+ } while(0)
+
namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
@@ -1057,6 +1068,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
// validate the transfer requested and populate dsts & extra
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
@@ -1109,6 +1122,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
@@ -1163,6 +1178,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
cryptonote::blobdata blob;
if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
{
@@ -1511,6 +1528,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
try
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions();
@@ -1539,6 +1558,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
// validate the transfer requested and populate dsts & extra
std::list<wallet_rpc::transfer_destination> destination;
destination.push_back(wallet_rpc::transfer_destination());
@@ -1604,6 +1625,8 @@ namespace tools
return false;
}
+ CHECK_MULTISIG_ENABLED();
+
// validate the transfer requested and populate dsts & extra
std::list<wallet_rpc::transfer_destination> destination;
destination.push_back(wallet_rpc::transfer_destination());
@@ -3933,6 +3956,9 @@ namespace tools
er.message = "This wallet is already multisig";
return false;
}
+ if (req.enable_multisig_experimental)
+ m_wallet->enable_multisig(true);
+ CHECK_MULTISIG_ENABLED();
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
@@ -3959,6 +3985,7 @@ namespace tools
er.message = "This wallet is already multisig";
return false;
}
+ CHECK_MULTISIG_ENABLED();
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
@@ -4003,6 +4030,7 @@ namespace tools
er.message = "This wallet is multisig, but not yet finalized";
return false;
}
+ CHECK_MULTISIG_ENABLED();
cryptonote::blobdata info;
try
@@ -4044,6 +4072,7 @@ namespace tools
er.message = "This wallet is multisig, but not yet finalized";
return false;
}
+ CHECK_MULTISIG_ENABLED();
if (req.info.size() < threshold - 1)
{
@@ -4096,6 +4125,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ CHECK_MULTISIG_ENABLED();
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -4123,6 +4153,7 @@ namespace tools
er.message = "This wallet is multisig, and already finalized";
return false;
}
+ CHECK_MULTISIG_ENABLED();
if (req.multisig_info.size() + 1 < total)
{
@@ -4172,6 +4203,7 @@ namespace tools
er.message = "This wallet is multisig, but not yet finalized";
return false;
}
+ CHECK_MULTISIG_ENABLED();
cryptonote::blobdata blob;
if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
@@ -4241,6 +4273,7 @@ namespace tools
er.message = "This wallet is multisig, but not yet finalized";
return false;
}
+ CHECK_MULTISIG_ENABLED();
cryptonote::blobdata blob;
if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index fe53e293f..ecfc8e673 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -2416,7 +2416,10 @@ namespace wallet_rpc
{
struct request_t
{
+ bool enable_multisig_experimental;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(enable_multisig_experimental, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 914939573..734229380 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -78,3 +78,4 @@
#define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45
#define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT -46
#define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47
+#define WALLET_RPC_ERROR_CODE_DISABLED -48
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 89cb2fdc7..1c5894f47 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -107,7 +107,7 @@ class MultisigTest():
try: self.wallet[i].close_wallet()
except: pass
res = self.wallet[i].restore_deterministic_wallet(seed = seeds[i])
- res = self.wallet[i].prepare_multisig()
+ res = self.wallet[i].prepare_multisig(enable_multisig_experimental = True)
assert len(res.multisig_info) > 0
info.append(res.multisig_info)
@@ -172,7 +172,7 @@ class MultisigTest():
res = wallet2of2[i].restore_deterministic_wallet(seed = seeds[i])
res = wallet2of2[i].is_multisig()
assert not res.multisig
- res = wallet2of2[i].prepare_multisig()
+ res = wallet2of2[i].prepare_multisig(enable_multisig_experimental = True)
assert len(res.multisig_info) > 0
info2of2.append(res.multisig_info)
@@ -187,7 +187,7 @@ class MultisigTest():
assert res.ready
ok = False
- try: res = wallet2of2[0].prepare_multisig()
+ try: res = wallet2of2[0].prepare_multisig(enable_multisig_experimental = True)
except: ok = True
assert ok
@@ -205,7 +205,7 @@ class MultisigTest():
res = wallet2of3[i].restore_deterministic_wallet(seed = seeds[i])
res = wallet2of3[i].is_multisig()
assert not res.multisig
- res = wallet2of3[i].prepare_multisig()
+ res = wallet2of3[i].prepare_multisig(enable_multisig_experimental = True)
assert len(res.multisig_info) > 0
info2of3.append(res.multisig_info)
@@ -223,7 +223,7 @@ class MultisigTest():
assert not res.ready
ok = False
- try: res = wallet2of3[1].prepare_multisig()
+ try: res = wallet2of3[1].prepare_multisig(enable_multisig_experimental = True)
except: ok = True
assert ok
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index 134fa6ece..6c8cd9f8d 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -1026,12 +1026,12 @@ TEST(node_server, race_condition)
}
void stop() {}
void on_connection_close(context_t &context) {}
- void set_max_out_peers(unsigned int max) {}
+ void set_max_out_peers(epee::net_utils::zone zone, unsigned int max) {}
bool no_sync() const { return {}; }
void set_no_sync(bool value) {}
string_t get_peers_overview() const { return {}; }
stripes_t get_next_needed_pruning_stripe() const { return {}; }
- bool needs_new_sync_connections() const { return {}; }
+ bool needs_new_sync_connections(epee::net_utils::zone zone) const { return {}; }
bool is_busy_syncing() { return {}; }
};
using node_server_t = nodetool::node_server<protocol_t>;
diff --git a/utils/build_scripts/windows.bat b/utils/build_scripts/windows.bat
deleted file mode 100644
index 77ffd1c96..000000000
--- a/utils/build_scripts/windows.bat
+++ /dev/null
@@ -1,45 +0,0 @@
-:: Copyright (c) 2014-2022, 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.
-
-:: Set the following variables according to your environment
-set BuildProcessorArchitecture=64
-set LocationDependencyBoostRoot=D:\Development\boost_1_55_0
-set LocationEnvironmentVariableSetterMsbuild=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
-
-call "%LocationEnvironmentVariableSetterMsbuild%"
-set LocationDependencyBoostLibrary=%LocationDependencyBoostRoot%\lib%BuildProcessorArchitecture%-msvc-%VisualStudioVersion%
-
-cd ..\..
-set LocationBuildSource=%CD%
-mkdir build\win%BuildProcessorArchitecture%
-cd build\win%BuildProcessorArchitecture%
-
-cmake -G "Visual Studio %VisualStudioVersion:.0=% Win%BuildProcessorArchitecture%" -DBOOST_ROOT="%LocationDependencyBoostRoot%" -DBOOST_LIBRARYDIR="%LocationDependencyBoostLibrary%" "%LocationBuildSource%"
-msbuild Project.sln /p:Configuration=Release
-
-pause
diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py
index 037beee84..01e937627 100644
--- a/utils/python-rpc/framework/wallet.py
+++ b/utils/python-rpc/framework/wallet.py
@@ -490,10 +490,11 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(is_multisig)
- def prepare_multisig(self):
+ def prepare_multisig(self, enable_multisig_experimental = False):
prepare_multisig = {
'method': 'prepare_multisig',
'params' : {
+ 'enable_multisig_experimental': enable_multisig_experimental,
},
'jsonrpc': '2.0',
'id': '0'