Enforce Tx unlock_time is Zero by Relay Rule [RELEASE]

Related to https://github.com/monero-project/research-lab/issues/78

Added a relay rule that enforces the `unlock_time` field is equal to 0 for non-coinbase transactions.

UIs changed:
* Removed `locked_transfer` and `locked_sweep_all` commands from `monero-wallet-cli`

APIs changed:
* Removed `unlock_time` parameters from `wallet2` transfer methods
* Wallet RPC transfer endpoints send error codes when requested unlock time is not 0
* Removed `unlock_time` parameters from `construct_tx*` cryptonote core functions

@tobtoht: undo rebase changes tx.dsts -> tx_dsts
This commit is contained in:
jeffro256 2024-02-03 21:59:58 -06:00
parent 81d4db08eb
commit dd47d03cf2
No known key found for this signature in database
GPG key ID: 6F79797A6E392442
38 changed files with 186 additions and 230 deletions

View file

@ -155,11 +155,6 @@ typedef cryptonote::simple_wallet sw;
} \
} while(0)
enum TransferType {
Transfer,
TransferLocked,
};
static std::string get_human_readable_timespan(std::chrono::seconds seconds);
static std::string get_human_readable_timespan(uint64_t seconds);
@ -197,8 +192,6 @@ namespace
const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
const char* USAGE_PAYMENT_ID("payment_id");
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [subtractfeefrom=<D0>[,<D1>,all,...]] [<payment_id>]");
const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_ACCOUNT("sweep_account <account> [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
@ -3336,14 +3329,6 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1),
tr(USAGE_TRANSFER),
tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer <addr1> 3 <addr2> 0.5 <addr3> 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 XMR including fees, and addr1 & addr3 would receive 2.925 & 0.975 XMR, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations."));
m_cmd_binder.set_handler("locked_transfer",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_transfer,_1),
tr(USAGE_LOCKED_TRANSFER),
tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_sweep_all",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1),
tr(USAGE_LOCKED_SWEEP_ALL),
tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1),
tr("Send all unmixable outputs to yourself with ring_size 1"));
@ -6588,7 +6573,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector<std:
return (this->*cmd)(args);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool called_by_mms)
{
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
if (!try_connect_to_daemon())
@ -6640,7 +6625,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
return false;
}
const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
const size_t min_args = 1;
if(local_args.size() < min_args)
{
fail_msg_writer() << tr("wrong number of arguments");
@ -6665,26 +6650,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
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 false;
}
if (locked_blocks > 1000000)
{
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
return false;
}
local_args.pop_back();
}
// Parse subtractfeefrom destination list
tools::wallet2::unique_index_container subtract_fee_from_outputs;
bool subtract_fee_from_all = false;
@ -6814,28 +6779,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
try
{
// 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 false;
}
unlock_block = bc_height + locked_blocks;
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
break;
default:
LOG_ERROR("Unknown transfer method, using default");
/* FALLTHRU */
case Transfer:
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
break;
}
auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra,
m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
if (ptx_vector.empty())
{
@ -6945,11 +6890,6 @@ 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 (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
}
if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return false;
bool default_ring_size = true;
@ -7069,29 +7009,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
PRINT_USAGE(USAGE_TRANSFER);
return true;
}
transfer_main(Transfer, args_, false);
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
{
if (args_.size() < 1)
{
PRINT_USAGE(USAGE_LOCKED_TRANSFER);
return true;
}
transfer_main(TransferLocked, args_, false);
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
{
if (args_.size() < 1)
{
PRINT_USAGE(USAGE_LOCKED_SWEEP_ALL);
return true;
}
sweep_main(m_current_subaddress_account, 0, true, args_);
transfer_main(args_, false);
return true;
}
//----------------------------------------------------------------------------------------------------
@ -7203,7 +7121,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args_)
bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args_)
{
auto print_usage = [this, account, below]()
{
@ -7283,41 +7201,6 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
return true;
}
uint64_t unlock_block = 0;
if (locked) {
uint64_t locked_blocks = 0;
if (local_args.size() < 2) {
fail_msg_writer() << tr("missing lockedblocks parameter");
return true;
}
try
{
locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]);
}
catch (const std::exception &e)
{
fail_msg_writer() << tr("bad locked_blocks parameter");
return true;
}
if (locked_blocks > 1000000)
{
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
return true;
}
std::string err;
uint64_t 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;
local_args.erase(local_args.begin() + 1);
}
size_t outputs = 1;
if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
{
@ -7392,7 +7275,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
try
{
// figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices);
auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra, account, subaddr_indices);
if (ptx_vector.empty())
{
@ -7649,7 +7532,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra);
if (ptx_vector.empty())
{
@ -7759,7 +7642,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
{
sweep_main(m_current_subaddress_account, 0, false, args_);
sweep_main(m_current_subaddress_account, 0, args_);
return true;
}
//----------------------------------------------------------------------------------------------------
@ -7779,7 +7662,7 @@ bool simple_wallet::sweep_account(const std::vector<std::string> &args_)
}
local_args.erase(local_args.begin());
sweep_main(account, 0, false, local_args);
sweep_main(account, 0, local_args);
return true;
}
//----------------------------------------------------------------------------------------------------
@ -7797,7 +7680,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
fail_msg_writer() << tr("invalid amount threshold");
return true;
}
sweep_main(m_current_subaddress_account, below, false, std::vector<std::string>(++args_.begin(), args_.end()));
sweep_main(m_current_subaddress_account, below, std::vector<std::string>(++args_.begin(), args_.end()));
return true;
}
//----------------------------------------------------------------------------------------------------
@ -11399,7 +11282,7 @@ void simple_wallet::mms_sync(const std::vector<std::string> &args)
void simple_wallet::mms_transfer(const std::vector<std::string> &args)
{
// It's too complicated to check any arguments here, just let 'transfer_main' do the whole job
transfer_main(Transfer, args, true);
transfer_main(args, true);
}
void simple_wallet::mms_delete(const std::vector<std::string> &args)