mirror of
https://github.com/monero-project/monero.git
synced 2025-04-17 11:42:00 +00:00
add multi-URI support
This commit is contained in:
parent
977dedce2c
commit
f753683748
12 changed files with 619 additions and 85 deletions
src
simplewallet
wallet
tests
utils/python-rpc/framework
|
@ -6532,25 +6532,50 @@ bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool ca
|
|||
bool r = true;
|
||||
|
||||
// check for a URI
|
||||
std::string address_uri, payment_id_uri, tx_description, recipient_name, error;
|
||||
std::string tx_description, error;
|
||||
std::vector<std::string> unknown_parameters;
|
||||
uint64_t amount = 0;
|
||||
bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters, error);
|
||||
std::vector<std::string> addresses, recipient_names;
|
||||
std::vector<uint64_t> amounts;
|
||||
bool has_uri = m_wallet->parse_uri(local_args[i], addresses, amounts, recipient_names, tx_description, unknown_parameters, error);
|
||||
if (has_uri)
|
||||
{
|
||||
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_uri, oa_prompter);
|
||||
if (payment_id_uri.size() == 16)
|
||||
bool payment_id_seen = false;
|
||||
for (size_t address_index = 0; address_index < addresses.size(); address_index++)
|
||||
{
|
||||
if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id))
|
||||
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), addresses[address_index], oa_prompter);
|
||||
if (info.has_payment_id)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse short payment ID from URI");
|
||||
return false;
|
||||
if (payment_id_seen)
|
||||
{
|
||||
fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extra_nonce;
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
||||
if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to set up payment id");
|
||||
return false;
|
||||
}
|
||||
payment_id_seen = true;
|
||||
}
|
||||
info.has_payment_id = true;
|
||||
|
||||
de.amount = amounts[address_index];
|
||||
de.original = addresses[address_index];
|
||||
if (!r)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse address");
|
||||
return false;
|
||||
}
|
||||
de.addr = info.address;
|
||||
de.is_subaddress = info.is_subaddress;
|
||||
de.is_integrated = info.has_payment_id;
|
||||
|
||||
dsts.push_back(de);
|
||||
}
|
||||
de.amount = amount;
|
||||
de.original = local_args[i];
|
||||
++i;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else if (i + 1 < local_args.size())
|
||||
{
|
||||
|
@ -6583,34 +6608,20 @@ bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool ca
|
|||
de.is_subaddress = info.is_subaddress;
|
||||
de.is_integrated = info.has_payment_id;
|
||||
|
||||
if (info.has_payment_id || !payment_id_uri.empty())
|
||||
if (info.has_payment_id)
|
||||
{
|
||||
if (payment_id_seen)
|
||||
{
|
||||
fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
|
||||
return false;
|
||||
fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::hash payment_id;
|
||||
std::string extra_nonce;
|
||||
if (info.has_payment_id)
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
||||
if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
|
||||
{
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
||||
}
|
||||
else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id))
|
||||
{
|
||||
LONG_PAYMENT_ID_SUPPORT_CHECK();
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse payment id, though it was detected");
|
||||
return false;
|
||||
}
|
||||
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
|
||||
if(!r)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
|
||||
return false;
|
||||
fail_msg_writer() << tr("failed to set up payment id");
|
||||
return false;
|
||||
}
|
||||
payment_id_seen = true;
|
||||
}
|
||||
|
|
|
@ -2553,11 +2553,21 @@ bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::st
|
|||
return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error);
|
||||
}
|
||||
|
||||
bool WalletImpl::parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error)
|
||||
{
|
||||
return m_wallet->parse_uri(uri, addresses, amounts, recipient_names, tx_description, unknown_parameters, error);
|
||||
}
|
||||
|
||||
std::string WalletImpl::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
|
||||
{
|
||||
return m_wallet->make_uri(address, payment_id, amount, tx_description, recipient_name, error);
|
||||
}
|
||||
|
||||
std::string WalletImpl::make_uri(const std::vector<std::string> &addresses, const std::vector<std::uint64_t> &amounts, const std::vector<std::string> &recipient_names, const std::string &tx_description, std::string &error) const
|
||||
{
|
||||
return m_wallet->make_uri(addresses, amounts, recipient_names, tx_description, error);
|
||||
}
|
||||
|
||||
std::string WalletImpl::getDefaultDataDir() const
|
||||
{
|
||||
return tools::get_default_data_dir();
|
||||
|
|
|
@ -214,7 +214,9 @@ public:
|
|||
virtual void startRefresh() override;
|
||||
virtual void pauseRefresh() override;
|
||||
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override;
|
||||
bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) override;
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override;
|
||||
std::string make_uri(const std::vector<std::string> &addresses, const std::vector<std::uint64_t> &amounts, const std::vector<std::string> &recipient_names, const std::string &tx_description, std::string &error) const override;
|
||||
virtual std::string getDefaultDataDir() const override;
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override;
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) override;
|
||||
|
|
|
@ -1066,8 +1066,11 @@ struct Wallet
|
|||
virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0;
|
||||
|
||||
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0;
|
||||
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
|
||||
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0;
|
||||
virtual std::string make_uri(const std::vector<std::string> &addresses, const std::vector<std::uint64_t> &amounts, const std::vector<std::string> &recipient_names, const std::string &tx_description, std::string &error) const = 0;
|
||||
|
||||
virtual std::string getDefaultDataDir() const = 0;
|
||||
|
||||
/*
|
||||
|
|
|
@ -14942,56 +14942,155 @@ std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext,
|
|||
return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
|
||||
std::string wallet2::custom_convert_to_url_format(const std::string &uri) const
|
||||
{
|
||||
cryptonote::address_parse_info info;
|
||||
if(!get_account_address_from_str(info, nettype(), address))
|
||||
std::string s = epee::net_utils::conver_to_url_format(uri);
|
||||
|
||||
// replace '=' with "%3D" and '?' with "%3F".
|
||||
std::string result;
|
||||
result.reserve(s.size());
|
||||
|
||||
for (char c : s)
|
||||
{
|
||||
error = std::string("wrong address: ") + address;
|
||||
if (c == '=')
|
||||
{
|
||||
result.append("%3D");
|
||||
}
|
||||
else if (c == '?')
|
||||
{
|
||||
result.append("%3F");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
result.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::make_uri(const std::vector<std::string> &addresses, const std::vector<std::uint64_t> &amounts, const std::vector<std::string> &recipient_names, const std::string &tx_description, std::string &error) const
|
||||
{
|
||||
if (addresses.empty())
|
||||
{
|
||||
error = "No recipient information like addresses are provided.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// we want only one payment id
|
||||
if (info.has_payment_id && !payment_id.empty())
|
||||
if (addresses.size() != amounts.size() || addresses.size() != recipient_names.size())
|
||||
{
|
||||
error = "A single payment id is allowed";
|
||||
error = (boost::format("The counts of addresses (%zu), amounts (%zu), and recipient names (%zu) do not match.") % addresses.size() % amounts.size() % recipient_names.size()).str();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (!payment_id.empty())
|
||||
std::string addresses_str = "";
|
||||
std::string amounts_str = "";
|
||||
bool amounts_used = false;
|
||||
std::string recipients_str = "";
|
||||
bool recipients_used = false;
|
||||
|
||||
for (size_t i = 0; i < addresses.size(); i++)
|
||||
{
|
||||
error = "Standalone payment id deprecated, use integrated address instead";
|
||||
return std::string();
|
||||
const std::string &address = addresses[i];
|
||||
cryptonote::address_parse_info info;
|
||||
if (!get_account_address_from_str(info, nettype(), address))
|
||||
{
|
||||
error = std::string("wrong address: ") + address;
|
||||
return std::string();
|
||||
}
|
||||
if (!addresses_str.empty())
|
||||
{
|
||||
addresses_str.append(";");
|
||||
}
|
||||
addresses_str += address;
|
||||
|
||||
if (!amounts_str.empty())
|
||||
{
|
||||
amounts_str.append(";");
|
||||
}
|
||||
if (amounts[i] > 0)
|
||||
{
|
||||
amounts_used = true;
|
||||
}
|
||||
amounts_str += cryptonote::print_money(amounts[i]);
|
||||
|
||||
if (!recipients_str.empty())
|
||||
{
|
||||
recipients_str.append(";");
|
||||
}
|
||||
if (!recipient_names[i].empty())
|
||||
{
|
||||
recipients_used = true;
|
||||
recipients_str += custom_convert_to_url_format(recipient_names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string uri = "monero:" + address;
|
||||
std::string uri = "monero:" + addresses_str;
|
||||
unsigned int n_fields = 0;
|
||||
|
||||
if (!payment_id.empty())
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("tx_payment_id=") + payment_id;
|
||||
}
|
||||
|
||||
if (amount > 0)
|
||||
if (amounts_used)
|
||||
{
|
||||
// URI encoded amount is in decimal units, not atomic units
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("tx_amount=") + cryptonote::print_money(amount);
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("tx_amount=") + amounts_str;
|
||||
}
|
||||
|
||||
if (!recipient_name.empty())
|
||||
if (recipients_used)
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + recipients_str;
|
||||
}
|
||||
|
||||
if (!tx_description.empty())
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
|
||||
uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + custom_convert_to_url_format(tx_description);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
|
||||
std::string wallet2::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
|
||||
{
|
||||
cryptonote::address_parse_info info;
|
||||
if (!get_account_address_from_str(info, nettype(), address))
|
||||
{
|
||||
error = std::string("wrong address: ") + address;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (info.has_payment_id && !payment_id.empty())
|
||||
{
|
||||
error = "A single payment id is allowed";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string uri = "monero:" + address;
|
||||
unsigned n_fields = 0;
|
||||
|
||||
if (!payment_id.empty())
|
||||
{
|
||||
error = "Standalone payment id deprecated, use integrated address instead";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + ("tx_amount=" + cryptonote::print_money(amount));
|
||||
}
|
||||
|
||||
if (!recipient_name.empty())
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + ("recipient_name=" + custom_convert_to_url_format(recipient_name));
|
||||
}
|
||||
|
||||
if (!tx_description.empty())
|
||||
{
|
||||
uri += (n_fields++ ? "&" : "?") + ("tx_description=" + custom_convert_to_url_format(tx_description));
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error)
|
||||
{
|
||||
if (uri.substr(0, 7) != "monero:")
|
||||
{
|
||||
|
@ -15001,24 +15100,37 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
|
|||
|
||||
std::string remainder = uri.substr(7);
|
||||
const char *ptr = strchr(remainder.c_str(), '?');
|
||||
address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
|
||||
std::string addresses_string = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
|
||||
boost::split(addresses, addresses_string, boost::is_any_of(";"));
|
||||
addresses.erase(std::remove(addresses.begin(), addresses.end(), ""), addresses.end());
|
||||
|
||||
cryptonote::address_parse_info info;
|
||||
if(!get_account_address_from_str(info, nettype(), address))
|
||||
if (addresses.empty())
|
||||
{
|
||||
error = std::string("URI has wrong address: ") + address;
|
||||
error = "No addresses are present in the given URI.";
|
||||
return false;
|
||||
}
|
||||
if (!strchr(remainder.c_str(), '?'))
|
||||
|
||||
for (const std::string &address : addresses)
|
||||
{
|
||||
cryptonote::address_parse_info info;
|
||||
if (!get_account_address_from_str(info, nettype(), address))
|
||||
{
|
||||
error = std::string("URI contains improper address: ") + address;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
amounts.assign(addresses.size(), 0);
|
||||
recipient_names.assign(addresses.size(), "");
|
||||
|
||||
if (ptr == nullptr)
|
||||
return true;
|
||||
|
||||
std::string params(ptr+1);
|
||||
std::vector<std::string> arguments;
|
||||
std::string body = remainder.substr(address.size() + 1);
|
||||
if (body.empty())
|
||||
return true;
|
||||
boost::split(arguments, body, boost::is_any_of("&"));
|
||||
boost::split(arguments, params, boost::is_any_of("&"));
|
||||
std::set<std::string> have_arg;
|
||||
for (const auto &arg: arguments)
|
||||
for (const std::string &arg: arguments)
|
||||
{
|
||||
std::vector<std::string> kv;
|
||||
boost::split(kv, arg, boost::is_any_of("="));
|
||||
|
@ -15036,31 +15148,40 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
|
|||
|
||||
if (kv[0] == "tx_amount")
|
||||
{
|
||||
amount = 0;
|
||||
if (!cryptonote::parse_amount(amount, kv[1]))
|
||||
std::vector<std::string> amounts_split;
|
||||
boost::split(amounts_split, kv[1], boost::is_any_of(";"));
|
||||
|
||||
if (amounts_split.size() != addresses.size())
|
||||
{
|
||||
error = std::string("URI has invalid amount: ") + kv[1];
|
||||
error = (boost::format("tx_amount count of %zu is not equal to address count of %zu") % amounts_split.size() % addresses.size()).str();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (kv[0] == "tx_payment_id")
|
||||
{
|
||||
if (info.has_payment_id)
|
||||
|
||||
for (size_t i = 0; i < amounts_split.size(); i++)
|
||||
{
|
||||
error = "Separate payment id given with an integrated address";
|
||||
return false;
|
||||
uint64_t amount;
|
||||
if (!cryptonote::parse_amount(amount, amounts_split[i]))
|
||||
{
|
||||
error = std::string("URI has invalid amount: ") + amounts_split[i];
|
||||
return false;
|
||||
}
|
||||
amounts[i] = amount;
|
||||
}
|
||||
crypto::hash hash;
|
||||
if (!wallet2::parse_long_payment_id(kv[1], hash))
|
||||
{
|
||||
error = "Invalid payment id: " + kv[1];
|
||||
return false;
|
||||
}
|
||||
payment_id = kv[1];
|
||||
}
|
||||
else if (kv[0] == "recipient_name")
|
||||
{
|
||||
recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
|
||||
std::vector<std::string> names_split;
|
||||
boost::split(names_split, kv[1], boost::is_any_of(";"));
|
||||
if (names_split.size() != addresses.size())
|
||||
{
|
||||
error = "Recipient name count does not match address count";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < names_split.size(); i++)
|
||||
{
|
||||
recipient_names[i] = epee::net_utils::convert_from_url_format(names_split[i]);
|
||||
}
|
||||
}
|
||||
else if (kv[0] == "tx_description")
|
||||
{
|
||||
|
@ -15071,6 +15192,66 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
|
|||
unknown_parameters.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!amounts.empty() && amounts.size() != addresses.size())
|
||||
{
|
||||
error = "Amount count does not match address count.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::parse_uri(const std::string& uri, std::string& address, std::string& payment_id, uint64_t& amount, std::string& tx_description, std::string& recipient_name, std::vector<std::string>& unknown_parameters, std::string& error)
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
std::vector<uint64_t> amounts;
|
||||
std::vector<std::string> recipient_names;
|
||||
|
||||
if (!parse_uri(uri, addresses, amounts, recipient_names, tx_description, unknown_parameters, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> new_unknown_parameters;
|
||||
for (const auto& unknown_parameter : unknown_parameters) {
|
||||
if (unknown_parameter.find("tx_payment_id=") != 0)
|
||||
{
|
||||
new_unknown_parameters.push_back(unknown_parameter);
|
||||
continue;
|
||||
}
|
||||
|
||||
payment_id = unknown_parameter.substr(strlen("tx_payment_id="));
|
||||
|
||||
cryptonote::address_parse_info info;
|
||||
if (!get_account_address_from_str(info, nettype(), addresses[0]))
|
||||
{
|
||||
error = "URI contains improper address: " + addresses[0];
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.has_payment_id)
|
||||
{
|
||||
error = "Separate payment id given with integrated address";
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::hash hash;
|
||||
if (!wallet2::parse_long_payment_id(payment_id, hash))
|
||||
{
|
||||
error = "Invalid payment id: " + payment_id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
unknown_parameters = std::move(new_unknown_parameters);
|
||||
|
||||
if (addresses.size() != 1) {
|
||||
error = "Multiple recipient addresses parsed out of transaction intended for one recipient";
|
||||
return false;
|
||||
}
|
||||
|
||||
address = addresses[0];
|
||||
amount = amounts.empty() ? 0 : amounts[0];
|
||||
recipient_name = recipient_names.empty() ? "" : recipient_names[0];
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -1641,8 +1641,11 @@ private:
|
|||
template<typename T=std::string> T decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated = true) const;
|
||||
|
||||
std::string custom_convert_to_url_format(const std::string &uri) const;
|
||||
std::string make_uri(const std::vector<std::string> &addresses, const std::vector<std::uint64_t> &amounts, const std::vector<std::string> &recipient_names, const std::string &tx_description, std::string &error) const;
|
||||
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const;
|
||||
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
|
||||
bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error);
|
||||
|
||||
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
|
||||
|
||||
|
|
|
@ -3142,6 +3142,22 @@ namespace tools
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_make_uri_v2(const wallet_rpc::COMMAND_RPC_MAKE_URI_V2::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI_V2::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
std::string error;
|
||||
std::string uri = m_wallet->make_uri(req.addresses, req.amounts, req.recipient_names, req.tx_description, error);
|
||||
if (uri.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
|
||||
er.message = std::string("Cannot make URI from supplied parameters: ") + error;
|
||||
return false;
|
||||
}
|
||||
|
||||
res.uri = uri;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
@ -3155,6 +3171,26 @@ namespace tools
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_parse_uri_v2(const wallet_rpc::COMMAND_RPC_PARSE_URI_V2::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI_V2::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
std::string error;
|
||||
std::vector<std::string> addresses;
|
||||
std::vector<std::uint64_t> amounts;
|
||||
std::vector<std::string> recipient_names;
|
||||
if (!m_wallet->parse_uri(req.uri, addresses, amounts, recipient_names, res.uri.tx_description, res.unknown_parameters, error))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
|
||||
er.message = "Error parsing URI: " + error;
|
||||
return false;
|
||||
}
|
||||
|
||||
res.uri.addresses = addresses;
|
||||
res.uri.amounts = amounts;
|
||||
res.uri.recipient_names = recipient_names;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
|
|
@ -126,7 +126,9 @@ namespace tools
|
|||
MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES)
|
||||
MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES)
|
||||
MAP_JON_RPC_WE("make_uri", on_make_uri, wallet_rpc::COMMAND_RPC_MAKE_URI)
|
||||
MAP_JON_RPC_WE("make_uri_v2", on_make_uri_v2, wallet_rpc::COMMAND_RPC_MAKE_URI_V2)
|
||||
MAP_JON_RPC_WE("parse_uri", on_parse_uri, wallet_rpc::COMMAND_RPC_PARSE_URI)
|
||||
MAP_JON_RPC_WE("parse_uri_v2", on_parse_uri_v2, wallet_rpc::COMMAND_RPC_PARSE_URI_V2)
|
||||
MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("edit_address_book", on_edit_address_book, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY)
|
||||
|
@ -222,7 +224,9 @@ namespace tools
|
|||
bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_make_uri_v2(const wallet_rpc::COMMAND_RPC_MAKE_URI_V2::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI_V2::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_parse_uri_v2(const wallet_rpc::COMMAND_RPC_PARSE_URI_V2::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI_V2::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
// advance which version they will stop working with
|
||||
// Don't go over 32767 for any of these
|
||||
#define WALLET_RPC_VERSION_MAJOR 1
|
||||
#define WALLET_RPC_VERSION_MINOR 28
|
||||
#define WALLET_RPC_VERSION_MINOR 29
|
||||
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
|
||||
namespace tools
|
||||
|
@ -1885,6 +1885,74 @@ namespace wallet_rpc
|
|||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct uri_spec_v2
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
std::vector<uint64_t> amounts;
|
||||
std::vector<std::string> recipient_names;
|
||||
std::string tx_description;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(addresses);
|
||||
KV_SERIALIZE(amounts);
|
||||
KV_SERIALIZE(recipient_names);
|
||||
KV_SERIALIZE(tx_description);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_MAKE_URI_V2
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
std::vector<uint64_t> amounts;
|
||||
std::vector<std::string> recipient_names;
|
||||
std::string tx_description;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(addresses);
|
||||
KV_SERIALIZE(amounts);
|
||||
KV_SERIALIZE(recipient_names);
|
||||
KV_SERIALIZE(tx_description);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string uri;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(uri)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_PARSE_URI_V2
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string uri;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(uri)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
uri_spec_v2 uri;
|
||||
std::vector<std::string> unknown_parameters;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(uri)
|
||||
KV_SERIALIZE(unknown_parameters)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY
|
||||
{
|
||||
struct request_t
|
||||
|
|
|
@ -43,6 +43,7 @@ class URITest():
|
|||
def run_test(self):
|
||||
self.create()
|
||||
self.test_monero_uri()
|
||||
self.test_monero_multi_uri()
|
||||
|
||||
def create(self):
|
||||
print('Creating wallet')
|
||||
|
@ -56,7 +57,7 @@ class URITest():
|
|||
assert res.seed == seed
|
||||
|
||||
def test_monero_uri(self):
|
||||
print('Testing monero: URI')
|
||||
print('Testing monero: URI - single')
|
||||
wallet = Wallet()
|
||||
|
||||
utf8string = [u'えんしゅう', u'あまやかす']
|
||||
|
@ -224,6 +225,104 @@ class URITest():
|
|||
assert res.unknown_parameters == [u'unknown=' + quoted_utf8string[0]], res
|
||||
|
||||
|
||||
def test_monero_multi_uri(self):
|
||||
print('Testing monero: URI - multiple')
|
||||
wallet = Wallet()
|
||||
addr1 = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
|
||||
addr2 = '4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY'
|
||||
addr3 = '8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm'
|
||||
utf8string = [u'えんしゅう', u'あまやかす']
|
||||
|
||||
self.test_multi_uri_two_payments(wallet, addr1, addr2, utf8string)
|
||||
self.test_multi_uri_three_payments(wallet, addr1, addr2, addr3, utf8string)
|
||||
self.test_multi_uri_with_mismatched_amounts(wallet, addr1, addr2)
|
||||
self.test_multi_uri_trailing_delimiter(wallet, addr1, addr2)
|
||||
self.test_multi_uri_special_characters(wallet, addr1, addr2)
|
||||
self.test_multi_uri_unknown_parameters(wallet, addr1)
|
||||
|
||||
def test_multi_uri_two_payments(self, wallet, addr1, addr2, utf8string):
|
||||
# build multi-recipient URI with two payments.
|
||||
addresses = [ addr1, addr2]
|
||||
amounts = [ 500000000000, 200000000000 ]
|
||||
recipient_names = [ utf8string[0], utf8string[1]]
|
||||
res = wallet.make_uri_v2(addresses=addresses, amounts=amounts, recipient_names=recipient_names, tx_description='Multi URI test with two payments')
|
||||
|
||||
# expect URI like:
|
||||
# monero:addr1;addr2?tx_amount=0.5;0.2&recipient_name=<url_encoded_name1>;<url_encoded_name2>&tx_description=Multi%20URI%20test%20with%20two%20payments
|
||||
parsed = wallet.parse_uri_v2(res.uri)
|
||||
# verify that both payments are present.
|
||||
assert len(parsed.uri.addresses) == 2, "Expected 2 payments in multi-recipient URI"
|
||||
assert parsed.uri.addresses[0] == addr1
|
||||
assert parsed.uri.amounts[0] == 500000000000
|
||||
assert parsed.uri.recipient_names[0] == utf8string[0]
|
||||
assert parsed.uri.addresses[1] == addr2
|
||||
assert parsed.uri.amounts[1] == 200000000000
|
||||
assert parsed.uri.recipient_names[1] == utf8string[1]
|
||||
# check tx_description at the top level.
|
||||
assert parsed.uri.tx_description == 'Multi URI test with two payments'
|
||||
|
||||
def test_multi_uri_three_payments(self, wallet, addr1, addr2, addr3, utf8string):
|
||||
# build multi-recipient URI with three payments.
|
||||
addresses = [ addr1, addr2, addr3 ]
|
||||
amounts = [ 1000000000000, 500000000000, 250000000000 ]
|
||||
recipient_names = [ utf8string[0], utf8string[1], '' ]
|
||||
res = wallet.make_uri_v2(addresses=addresses, amounts=amounts, recipient_names=recipient_names, tx_description='Multi URI test with three payments')
|
||||
parsed = wallet.parse_uri_v2(res.uri)
|
||||
assert len(parsed.uri.addresses) == 3, "Expected 3 payments in multi-recipient URI"
|
||||
assert parsed.uri.addresses[0] == addr1
|
||||
assert parsed.uri.amounts[0] == 1000000000000
|
||||
assert parsed.uri.recipient_names[0] == utf8string[0]
|
||||
assert parsed.uri.addresses[1] == addr2
|
||||
assert parsed.uri.amounts[1] == 500000000000
|
||||
assert parsed.uri.recipient_names[1] == utf8string[1]
|
||||
assert parsed.uri.addresses[2] == addr3
|
||||
assert parsed.uri.amounts[2] == 250000000000
|
||||
assert parsed.uri.recipient_names[2] == ''
|
||||
assert parsed.uri.tx_description == 'Multi URI test with three payments'
|
||||
|
||||
def test_multi_uri_with_mismatched_amounts(self, wallet, addr1, addr2):
|
||||
# manually build a URI with mismatched amounts (remove Bob's amount).
|
||||
# simulate this by concatenating amounts incorrectly.
|
||||
# (this step assumes you have control over the output URI; in practice, the server would reject it. For testing, we assume the RPC returns an error.)
|
||||
uri_bad = 'monero:' + addr1 + ';' + addr2 + '?tx_amount=0.5&recipient_name=Alice;Bob'
|
||||
ok = False
|
||||
try:
|
||||
wallet.parse_uri_v2(uri_bad)
|
||||
except Exception:
|
||||
ok = True
|
||||
assert ok, "Expected rejection for mismatched payment counts"
|
||||
|
||||
def test_multi_uri_trailing_delimiter(self, wallet, addr1, addr2):
|
||||
# case: trailing semicolon in addresses or parameters should be handled gracefully
|
||||
uri_trailing = 'monero:' + addr1 + ';' + addr2 + ';' + '?tx_amount=0.5;0.2&recipient_name=Alice;Bob'
|
||||
# depending on the implementation, a trailing empty value might be dropped.
|
||||
parsed = wallet.parse_uri_v2(uri_trailing)
|
||||
assert len(parsed.uri.addresses) == 2, "Trailing delimiter should not add empty payment"
|
||||
|
||||
def test_multi_uri_special_characters(self, wallet, addr1, addr2):
|
||||
# case: special characters in recipient names and descriptions
|
||||
special_name = "A&B=Test?"
|
||||
special_desc = "Desc with spaces & symbols!"
|
||||
addresses = [ addr1, addr2]
|
||||
amounts = [ 750000000000, 250000000000 ]
|
||||
recipient_names = [ special_name, special_name]
|
||||
|
||||
# the RPC should URL-encode these parameters.
|
||||
res = wallet.make_uri_v2(addresses=addresses, amounts=amounts, recipient_names=recipient_names, tx_description=special_desc)
|
||||
parsed = wallet.parse_uri_v2(res.uri)
|
||||
# check that the decoded values match the original.
|
||||
for recipient_name in parsed.uri.recipient_names:
|
||||
assert recipient_name == special_name, "Special characters in recipient name mismatch"
|
||||
assert parsed.uri.tx_description == special_desc, "Special characters in description mismatch"
|
||||
|
||||
def test_multi_uri_unknown_parameters(self, wallet, addr1):
|
||||
# build a well-formed multi-recipient URI and tack on unknown parameters.
|
||||
uri_with_unknown = 'monero:' + addr1 + '?tx_amount=239.39014&foo=bar&baz=quux'
|
||||
parsed = wallet.parse_uri_v2(uri_with_unknown)
|
||||
assert parsed.uri.addresses[0] == addr1
|
||||
assert parsed.uri.amounts[0] == 239390140000000
|
||||
# unknown parameters should be collected in the unknown_parameters list.
|
||||
assert parsed.unknown_parameters == ['foo=bar', 'baz=quux'], "Unknown parameters mismatch"
|
||||
|
||||
if __name__ == '__main__':
|
||||
URITest().run_test()
|
||||
|
|
|
@ -41,6 +41,17 @@
|
|||
bool ret = w.parse_uri(uri, address, payment_id, amount, description, recipient_name, unknown_parameters, error); \
|
||||
ASSERT_EQ(ret, expected);
|
||||
|
||||
#define PARSE_MULTI_URI(uri, expected) \
|
||||
std::vector<std::string> addresses; \
|
||||
std::vector<uint64_t> amounts; \
|
||||
std::vector<std::string> recipient_names; \
|
||||
std::string description, error; \
|
||||
std::vector<std::string> unknown_parameters; \
|
||||
tools::wallet2 w(cryptonote::TESTNET); \
|
||||
bool ret = w.parse_uri(uri, addresses, amounts, recipient_names, \
|
||||
description, unknown_parameters, error); \
|
||||
ASSERT_EQ(ret, expected);
|
||||
|
||||
TEST(uri, empty_string)
|
||||
{
|
||||
PARSE_URI("", false);
|
||||
|
@ -213,3 +224,84 @@ TEST(uri, url_encoded_once)
|
|||
ASSERT_EQ(description, "foo 20");
|
||||
}
|
||||
|
||||
|
||||
TEST(uri, multiple_addresses_no_params)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS, true);
|
||||
ASSERT_EQ(addresses.size(), 2);
|
||||
ASSERT_EQ(addresses[0], TEST_ADDRESS);
|
||||
ASSERT_EQ(addresses[1], TEST_ADDRESS);
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_amounts)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_amount=0.5;0.2", true);
|
||||
ASSERT_EQ(addresses.size(), 2);
|
||||
ASSERT_EQ(addresses[0], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[0], 500000000000);
|
||||
ASSERT_EQ(addresses[1], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[1], 200000000000);
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_recipient_names)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?recipient_name=Alice;Bob", true);
|
||||
ASSERT_EQ(addresses.size(), 2);
|
||||
ASSERT_EQ(addresses[0], TEST_ADDRESS);
|
||||
ASSERT_EQ(recipient_names[0], "Alice");
|
||||
ASSERT_EQ(addresses[1], TEST_ADDRESS);
|
||||
ASSERT_EQ(recipient_names[1], "Bob");
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_mismatched_amounts)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_amount=0.5", false);
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_mismatched_recipient_names)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?recipient_name=Alice", false);
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_partial_params)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_amount=0.5;0&recipient_name=Alice;", true);
|
||||
ASSERT_EQ(addresses.size(), 2);
|
||||
ASSERT_EQ(addresses[0], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[0], 500000000000);
|
||||
ASSERT_EQ(recipient_names[0], "Alice");
|
||||
ASSERT_EQ(addresses[1], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[1], 0);
|
||||
ASSERT_EQ(recipient_names[1], "");
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_unknown_params)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?unknown_param=123;456", true);
|
||||
ASSERT_EQ(unknown_parameters.size(), 1);
|
||||
ASSERT_EQ(unknown_parameters[0], "unknown_param=123;456");
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_with_description)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_description=Payment%20for%20services", true);
|
||||
ASSERT_EQ(description, "Payment for services");
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_mismatched_params)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_amount=0.5&recipient_name=Alice", false);
|
||||
}
|
||||
|
||||
TEST(uri, multiple_addresses_all_params_correct)
|
||||
{
|
||||
PARSE_MULTI_URI("monero:" TEST_ADDRESS ";" TEST_ADDRESS "?tx_amount=0.5;0.2&recipient_name=Alice;Bob&tx_description=Payment%20for%20services", true);
|
||||
ASSERT_EQ(addresses.size(), 2);
|
||||
ASSERT_EQ(addresses[0], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[0], 500000000000);
|
||||
ASSERT_EQ(recipient_names[0], "Alice");
|
||||
ASSERT_EQ(addresses[1], TEST_ADDRESS);
|
||||
ASSERT_EQ(amounts[1], 200000000000);
|
||||
ASSERT_EQ(recipient_names[1], "Bob");
|
||||
ASSERT_EQ(description, "Payment for services");
|
||||
}
|
||||
|
|
|
@ -1002,6 +1002,31 @@ class Wallet(object):
|
|||
}
|
||||
return self.rpc.send_json_rpc_request(make_uri)
|
||||
|
||||
def make_uri_v2(self, addresses, amounts, recipient_names, tx_description):
|
||||
make_uri_v2 = {
|
||||
'method': 'make_uri_v2',
|
||||
'jsonrpc': '2.0',
|
||||
'params': {
|
||||
'addresses': addresses,
|
||||
'amounts': amounts,
|
||||
'recipient_names': recipient_names,
|
||||
'tx_description': tx_description,
|
||||
},
|
||||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(make_uri_v2)
|
||||
|
||||
def parse_uri_v2(self, uri):
|
||||
parse_uri = {
|
||||
'method': 'parse_uri_v2',
|
||||
'jsonrpc': '2.0',
|
||||
'params': {
|
||||
'uri': uri,
|
||||
},
|
||||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(parse_uri)
|
||||
|
||||
def parse_uri(self, uri):
|
||||
parse_uri = {
|
||||
'method': 'parse_uri',
|
||||
|
|
Loading…
Reference in a new issue