diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 8621e1696..968efc52d 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -169,6 +169,7 @@ #define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_MIN_MIXIN_10 8 +#define HF_VERSION_MIN_MIXIN_15 15 #define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_PER_BYTE_FEE 8 #define HF_VERSION_SMALLER_BP 10 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 5f9db676c..ed47f9433 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3309,7 +3309,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, size_t n_unmixable = 0, n_mixable = 0; size_t min_actual_mixin = std::numeric_limits::max(); size_t max_actual_mixin = 0; - const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_15 ? 15 : hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -3352,14 +3352,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10)) - { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be 11"); - tvc.m_low_mixin = true; - return false; - } - - if (min_actual_mixin < min_mixin) + // The only circumstance where ring sizes less than expected are + // allowed is when spending unmixable non-RCT outputs in the chain. + // Caveat: at HF_VERSION_MIN_MIXIN_15, temporarily allow ring sizes + // of 11 to allow a grace period in the transition to larger ring size. + if (min_actual_mixin < min_mixin && !(hf_version == HF_VERSION_MIN_MIXIN_15 && min_actual_mixin == 10)) { if (n_unmixable == 0) { @@ -3373,6 +3370,15 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tvc.m_low_mixin = true; return false; } + } else if ((hf_version > HF_VERSION_MIN_MIXIN_15 && min_actual_mixin > 15) + || (hf_version == HF_VERSION_MIN_MIXIN_15 && min_actual_mixin != 15 && min_actual_mixin != 10) // grace period to allow either 15 or 10 + || (hf_version < HF_VERSION_MIN_MIXIN_15 && hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10) + || ((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) + ) + { + MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be " << (min_mixin + 1)); + tvc.m_low_mixin = true; + return false; } // min/max tx version based on HF, and we accept v1 txes if having a non mixable diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 38cf6f757..a98a827ca 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -99,10 +99,6 @@ typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" -#define DEFAULT_MIX 10 - -#define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol - #define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days #define LOCK_IDLE_SCOPE() \ @@ -2472,59 +2468,6 @@ bool simple_wallet::set_store_tx_info(const std::vector &args/* = s return true; } -bool simple_wallet::set_default_ring_size(const std::vector &args/* = std::vector()*/) -{ - if (m_wallet->watch_only()) - { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); - return true; - } - try - { - if (strchr(args[1].c_str(), '-')) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - uint32_t ring_size = boost::lexical_cast(args[1]); - if (ring_size < MIN_RING_SIZE && ring_size != 0) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - - if (ring_size != 0 && ring_size != DEFAULT_MIX+1) - { - if (m_wallet->use_fork_rules(8, 0)) - { - message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored."); - } - else - { - message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); - } - } - - const auto pwd_container = get_and_verify_password(); - if (pwd_container) - { - m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); - } - return true; - } - catch(const boost::bad_lexical_cast &) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - catch(...) - { - fail_msg_writer() << tr("could not change default ring size"); - return true; - } -} - bool simple_wallet::set_default_priority(const std::vector &args/* = std::vector()*/) { uint32_t priority = 0; @@ -3386,8 +3329,6 @@ simple_wallet::simple_wallet() " Whether to print detailed information about ring members during confirmation.\n " "store-tx-info <1|0>\n " " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n " - "default-ring-size \n " - " Set the default ring size (obsolete).\n " "auto-refresh <1|0>\n " " Whether to automatically synchronize new blocks from the daemon.\n " "refresh-type \n " @@ -3896,7 +3837,6 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1")); - CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE); CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); @@ -6539,7 +6479,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectoradjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + const size_t min_ring_size = m_wallet->get_min_ring_size(); + size_t fake_outs_count = min_ring_size - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -6862,7 +6803,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector(vin); - if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1) + if (in_to_key.key_offsets.size() != min_ring_size) default_ring_size = false; } } @@ -7140,7 +7081,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co priority = m_wallet->adjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + size_t fake_outs_count = m_wallet->get_min_ring_size() - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -7417,7 +7358,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) priority = m_wallet->adjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + size_t fake_outs_count = m_wallet->get_min_ring_size() - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 473120eac..4c005c53a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -125,7 +125,6 @@ namespace cryptonote bool set_always_confirm_transfers(const std::vector &args = std::vector()); bool set_print_ring_members(const std::vector &args = std::vector()); bool set_store_tx_info(const std::vector &args = std::vector()); - bool set_default_ring_size(const std::vector &args = std::vector()); bool set_auto_refresh(const std::vector &args = std::vector()); bool set_refresh_type(const std::vector &args = std::vector()); bool set_confirm_missing_payment_id(const std::vector &args = std::vector()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7b97a44d5..f51906e6e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7380,6 +7380,8 @@ int wallet2::get_fee_algorithm() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_min_ring_size() { + if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0)) + return 16; if (use_fork_rules(8, 10)) return 11; if (use_fork_rules(7, 10)) @@ -7393,6 +7395,8 @@ uint64_t wallet2::get_min_ring_size() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_max_ring_size() { + if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0)) + return 16; if (use_fork_rules(8, 10)) return 11; return 0; diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index 2233f19e7..31d5780bb 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -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, get_tx_key = False) + res = self.hot_wallet.transfer([dst], ring_size = 16, get_tx_key = False) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 0 @@ -121,7 +121,7 @@ class ColdSigningTest(): desc = res.desc[0] assert desc.amount_in >= amount + fee assert desc.amount_out == desc.amount_in - fee - assert desc.ring_size == 11 + assert desc.ring_size == 16 assert desc.unlock_time == 0 assert desc.payment_id in ['', '0000000000000000'] assert desc.change_amount == desc.amount_in - 1000000000000 - fee diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index d3b7a9592..41c55e4d4 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -51,7 +51,7 @@ namespace const command_line::arg_descriptor arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"}; const command_line::arg_descriptor arg_transfer_amount = {"transfer_amount", "", 60000000000000}; - const command_line::arg_descriptor arg_mix_in_factor = {"mix-in-factor", "", 10}; + const command_line::arg_descriptor arg_mix_in_factor = {"mix-in-factor", "", 15}; const command_line::arg_descriptor arg_tx_count = {"tx-count", "", 100}; const command_line::arg_descriptor arg_tx_per_second = {"tx-per-second", "", 20}; const command_line::arg_descriptor arg_test_repeat_count = {"test_repeat_count", "", 1}; diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index 17b94494a..c1b50b1e2 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -44,7 +44,7 @@ class MultisigTest(): self.mine('41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', 5) self.mine('44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', 5) self.mine('47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', 5) - self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60) + self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) self.test_states() @@ -261,7 +261,7 @@ class MultisigTest(): desc = res.desc[0] assert desc.amount_in >= amount + fee assert desc.amount_out == desc.amount_in - fee - assert desc.ring_size == 11 + assert desc.ring_size == 16 assert desc.unlock_time == 0 assert not 'payment_id' in desc or desc.payment_id in ['', '0000000000000000'] assert desc.change_amount == desc.amount_in - 1000000000000 - fee diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 5314b045d..dd15369d3 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -82,11 +82,11 @@ class TransferTest(): res = daemon.get_info() height = res.height - daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 100) for i in range(len(self.wallet)): self.wallet[i].refresh() res = self.wallet[i].get_height() - assert res.height == height + 80 + assert res.height == height + 100 def transfer(self): daemon = Daemon() @@ -110,23 +110,23 @@ class TransferTest(): print ('Checking short payment IDs cannot be used when not in an integrated address') ok = False - try: self.wallet[0].transfer([dst], ring_size = 11, payment_id = '1234567812345678', get_tx_key = False) + try: self.wallet[0].transfer([dst], ring_size = 16, payment_id = '1234567812345678', get_tx_key = False) 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) + try: self.wallet[0].transfer([dst], ring_size = 16, 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) + try: self.wallet[0].transfer([], ring_size = 16, get_tx_key = False) except: ok = True assert ok - res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = False, get_tx_hex = True) + res = self.wallet[0].transfer([dst], ring_size = 16, 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 @@ -231,7 +231,7 @@ class TransferTest(): print("Creating transfer to another, manual relay") dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1000000000000} - res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = True, do_not_relay = True, get_tx_hex = True) + res = self.wallet[0].transfer([dst], ring_size = 16, 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 @@ -321,7 +321,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, get_tx_key = True) + res = self.wallet[0].transfer([dst0, dst1, dst2], ring_size = 16, get_tx_key = True) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 32*2