Merge pull request #8178

5d388eb Bump ring size to 16 for v15 & remove set default in wallet cli (j-berman)
This commit is contained in:
luigi1111 2022-04-25 10:18:26 -05:00
commit e13244ea9c
No known key found for this signature in database
GPG key ID: F4ACA0183641E010
9 changed files with 38 additions and 87 deletions

View file

@ -169,6 +169,7 @@
#define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_4 6
#define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_MIN_MIXIN_6 7
#define HF_VERSION_MIN_MIXIN_10 8 #define HF_VERSION_MIN_MIXIN_10 8
#define HF_VERSION_MIN_MIXIN_15 15
#define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_ENFORCE_RCT 6
#define HF_VERSION_PER_BYTE_FEE 8 #define HF_VERSION_PER_BYTE_FEE 8
#define HF_VERSION_SMALLER_BP 10 #define HF_VERSION_SMALLER_BP 10

View file

@ -3321,7 +3321,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
size_t n_unmixable = 0, n_mixable = 0; size_t n_unmixable = 0, n_mixable = 0;
size_t min_actual_mixin = std::numeric_limits<size_t>::max(); size_t min_actual_mixin = std::numeric_limits<size_t>::max();
size_t max_actual_mixin = 0; 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) for (const auto& txin : tx.vin)
{ {
// non txin_to_key inputs will be rejected below // non txin_to_key inputs will be rejected below
@ -3364,14 +3364,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)) // The only circumstance where ring sizes less than expected are
{ // allowed is when spending unmixable non-RCT outputs in the chain.
MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be 11"); // Caveat: at HF_VERSION_MIN_MIXIN_15, temporarily allow ring sizes
tvc.m_low_mixin = true; // of 11 to allow a grace period in the transition to larger ring size.
return false; if (min_actual_mixin < min_mixin && !(hf_version == HF_VERSION_MIN_MIXIN_15 && min_actual_mixin == 10))
}
if (min_actual_mixin < min_mixin)
{ {
if (n_unmixable == 0) if (n_unmixable == 0)
{ {
@ -3385,6 +3382,15 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
tvc.m_low_mixin = true; tvc.m_low_mixin = true;
return false; 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 // min/max tx version based on HF, and we accept v1 txes if having a non mixable

View file

@ -99,10 +99,6 @@ typedef cryptonote::simple_wallet sw;
#define EXTENDED_LOGS_FILE "wallet_details.log" #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 OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days
#define LOCK_IDLE_SCOPE() \ #define LOCK_IDLE_SCOPE() \
@ -2472,59 +2468,6 @@ bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = s
return true; return true;
} }
bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
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<uint32_t>(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<std::string> &args/* = std::vector<std::string>()*/) bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{ {
uint32_t priority = 0; uint32_t priority = 0;
@ -3386,8 +3329,6 @@ simple_wallet::simple_wallet()
" Whether to print detailed information about ring members during confirmation.\n " " Whether to print detailed information about ring members during confirmation.\n "
"store-tx-info <1|0>\n " "store-tx-info <1|0>\n "
" Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n " " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n "
"default-ring-size <n>\n "
" Set the default ring size (obsolete).\n "
"auto-refresh <1|0>\n " "auto-refresh <1|0>\n "
" Whether to automatically synchronize new blocks from the daemon.\n " " Whether to automatically synchronize new blocks from the daemon.\n "
"refresh-type <full|optimize-coinbase|no-coinbase|default>\n " "refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
@ -3896,7 +3837,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); 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("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("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("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("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(", ")); 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::vector<std::stri
priority = m_wallet->adjust_priority(priority); priority = m_wallet->adjust_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) { if(local_args.size() > 0) {
size_t ring_size; size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) 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<std::stri
if (vin.type() == typeid(txin_to_key)) if (vin.type() == typeid(txin_to_key))
{ {
const txin_to_key& in_to_key = boost::get<txin_to_key>(vin); const txin_to_key& in_to_key = boost::get<txin_to_key>(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; 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); 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) { if(local_args.size() > 0) {
size_t ring_size; size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) 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<std::string> &args_)
priority = m_wallet->adjust_priority(priority); 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) { if(local_args.size() > 0) {
size_t ring_size; size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))

View file

@ -125,7 +125,6 @@ namespace cryptonote
bool set_always_confirm_transfers(const std::vector<std::string> &args = std::vector<std::string>()); bool set_always_confirm_transfers(const std::vector<std::string> &args = std::vector<std::string>());
bool set_print_ring_members(const std::vector<std::string> &args = std::vector<std::string>()); bool set_print_ring_members(const std::vector<std::string> &args = std::vector<std::string>());
bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>()); bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>());
bool set_default_ring_size(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>()); bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>());
bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>()); bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>()); bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>());

View file

@ -7406,6 +7406,8 @@ int wallet2::get_fee_algorithm()
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size() 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)) if (use_fork_rules(8, 10))
return 11; return 11;
if (use_fork_rules(7, 10)) if (use_fork_rules(7, 10))
@ -7419,6 +7421,8 @@ uint64_t wallet2::get_min_ring_size()
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_max_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)) if (use_fork_rules(8, 10))
return 11; return 11;
return 0; return 0;

View file

@ -101,7 +101,7 @@ class ColdSigningTest():
res = self.cold_wallet.export_key_images(True) res = self.cold_wallet.export_key_images(True)
self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset) 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 assert len(res.tx_hash) == 32*2
txid = res.tx_hash txid = res.tx_hash
assert len(res.tx_key) == 0 assert len(res.tx_key) == 0
@ -121,7 +121,7 @@ class ColdSigningTest():
desc = res.desc[0] desc = res.desc[0]
assert desc.amount_in >= amount + fee assert desc.amount_in >= amount + fee
assert desc.amount_out == desc.amount_in - 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.unlock_time == 0
assert desc.payment_id in ['', '0000000000000000'] assert desc.payment_id in ['', '0000000000000000']
assert desc.change_amount == desc.amount_in - 1000000000000 - fee assert desc.change_amount == desc.amount_in - 1000000000000 - fee

View file

@ -51,7 +51,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"}; const command_line::arg_descriptor<std::string> arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"};
const command_line::arg_descriptor<uint64_t> arg_transfer_amount = {"transfer_amount", "", 60000000000000}; const command_line::arg_descriptor<uint64_t> arg_transfer_amount = {"transfer_amount", "", 60000000000000};
const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 10}; const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 15};
const command_line::arg_descriptor<size_t> arg_tx_count = {"tx-count", "", 100}; const command_line::arg_descriptor<size_t> arg_tx_count = {"tx-count", "", 100};
const command_line::arg_descriptor<size_t> arg_tx_per_second = {"tx-per-second", "", 20}; const command_line::arg_descriptor<size_t> arg_tx_per_second = {"tx-per-second", "", 20};
const command_line::arg_descriptor<size_t> arg_test_repeat_count = {"test_repeat_count", "", 1}; const command_line::arg_descriptor<size_t> arg_test_repeat_count = {"test_repeat_count", "", 1};

View file

@ -44,7 +44,7 @@ class MultisigTest():
self.mine('41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', 5) self.mine('41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', 5)
self.mine('44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', 5) self.mine('44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', 5)
self.mine('47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', 5) self.mine('47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', 5)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60) self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80)
self.test_states() self.test_states()
@ -261,7 +261,7 @@ class MultisigTest():
desc = res.desc[0] desc = res.desc[0]
assert desc.amount_in >= amount + fee assert desc.amount_in >= amount + fee
assert desc.amount_out == desc.amount_in - 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.unlock_time == 0
assert not 'payment_id' in desc or desc.payment_id in ['', '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_amount == desc.amount_in - 1000000000000 - fee

View file

@ -82,11 +82,11 @@ class TransferTest():
res = daemon.get_info() res = daemon.get_info()
height = res.height height = res.height
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 100)
for i in range(len(self.wallet)): for i in range(len(self.wallet)):
self.wallet[i].refresh() self.wallet[i].refresh()
res = self.wallet[i].get_height() res = self.wallet[i].get_height()
assert res.height == height + 80 assert res.height == height + 100
def transfer(self): def transfer(self):
daemon = Daemon() daemon = Daemon()
@ -110,23 +110,23 @@ class TransferTest():
print ('Checking short payment IDs cannot be used when not in an integrated address') print ('Checking short payment IDs cannot be used when not in an integrated address')
ok = False 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 except: ok = True
assert ok assert ok
print ('Checking long payment IDs are rejected') print ('Checking long payment IDs are rejected')
ok = False 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 except: ok = True
assert ok assert ok
print ('Checking empty destination is rejected') print ('Checking empty destination is rejected')
ok = False 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 except: ok = True
assert ok 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 assert len(res.tx_hash) == 32*2
txid = res.tx_hash txid = res.tx_hash
assert len(res.tx_key) == 0 assert len(res.tx_key) == 0
@ -231,7 +231,7 @@ class TransferTest():
print("Creating transfer to another, manual relay") print("Creating transfer to another, manual relay")
dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1000000000000} 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 assert len(res.tx_hash) == 32*2
txid = res.tx_hash txid = res.tx_hash
assert len(res.tx_key) == 32*2 assert len(res.tx_key) == 32*2
@ -321,7 +321,7 @@ class TransferTest():
dst0 = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000} dst0 = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000}
dst1 = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1100000000000} dst1 = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1100000000000}
dst2 = {'address': '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 'amount': 1200000000000} 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 assert len(res.tx_hash) == 32*2
txid = res.tx_hash txid = res.tx_hash
assert len(res.tx_key) == 32*2 assert len(res.tx_key) == 32*2