From 99580adf66b7d2f9d744f8d8a162de1b88955ddf Mon Sep 17 00:00:00 2001 From: kenshi84 Date: Fri, 16 Dec 2016 12:06:50 +0900 Subject: [PATCH] make openalias also available for solo miner; introduce namespace tools::dns_utils; support integrated address with dns lookup --- src/common/dns_utils.cpp | 143 +++++++++++++++++++++++++ src/common/dns_utils.h | 19 ++++ src/daemon/command_parser_executor.cpp | 32 ++++-- src/simplewallet/simplewallet.cpp | 78 +------------- src/simplewallet/simplewallet.h | 1 - src/wallet/api/wallet_manager.cpp | 3 +- src/wallet/wallet2.cpp | 68 ------------ src/wallet/wallet2.h | 4 - tests/unit_tests/address_from_url.cpp | 17 +-- 9 files changed, 203 insertions(+), 162 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index e6e53a5c0..35fb9fe6c 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -26,7 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "common/command_line.h" +#include "common/i18n.h" #include "common/dns_utils.h" +#include "cryptonote_core/cryptonote_basic_impl.h" #include #include // check local first (in the event of static or in-source compilation of libunbound) @@ -323,4 +326,144 @@ bool DNSResolver::check_address_syntax(const char *addr) const return true; } +namespace dns_utils +{ + +const char *tr(const char *str) { return i18n_translate(str, "tools::dns_utils"); } + +//----------------------------------------------------------------------- +// TODO: parse the string in a less stupid way, probably with regex +std::string address_from_txt_record(const std::string& s) +{ + // make sure the txt record has "oa1:xmr" and find it + auto pos = s.find("oa1:xmr"); + if (pos == std::string::npos) + return {}; + // search from there to find "recipient_address=" + pos = s.find("recipient_address=", pos); + if (pos == std::string::npos) + return {}; + pos += 18; // move past "recipient_address=" + // find the next semicolon + auto pos2 = s.find(";", pos); + if (pos2 != std::string::npos) + { + // length of address == 95, we can at least validate that much here + if (pos2 - pos == 95) + { + return s.substr(pos, 95); + } + else if (pos2 - pos == 106) // length of address == 106 --> integrated address + { + return s.substr(pos, 106); + } + } + return {}; +} +/** + * @brief gets a monero address from the TXT record of a DNS entry + * + * gets the monero address from the TXT record of the DNS entry associated + * with . If this lookup fails, or the TXT record does not contain an + * XMR address in the correct format, returns an empty string. + * will be set true or false according to whether or not the DNS query passes + * DNSSEC validation. + * + * @param url the url to look up + * @param dnssec_valid return-by-reference for DNSSEC status of query + * + * @return a monero address (as a string) or an empty string + */ +std::vector addresses_from_url(const std::string& url, bool& dnssec_valid) +{ + std::vector addresses; + // get txt records + bool dnssec_available, dnssec_isvalid; + std::string oa_addr = DNSResolver::instance().get_dns_format_from_oa_address(url); + auto records = DNSResolver::instance().get_txt_record(oa_addr, dnssec_available, dnssec_isvalid); + + // TODO: update this to allow for conveying that dnssec was not available + if (dnssec_available && dnssec_isvalid) + { + dnssec_valid = true; + } + else dnssec_valid = false; + + // for each txt record, try to find a monero address in it. + for (auto& rec : records) + { + std::string addr = address_from_txt_record(rec); + if (addr.size()) + { + addresses.push_back(addr); + } + } + return addresses; +} + +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid) +{ + // attempt to get address from dns query + auto addresses = addresses_from_url(url, dnssec_valid); + if (addresses.empty()) + { + std::cout << tr("wrong address: ") << url; + return {}; + } + // for now, move on only if one address found + if (addresses.size() > 1) + { + std::cout << tr("not yet supported: Multiple Monero addresses found for given URL: ") << url; + return {}; + } + // prompt user for confirmation. + // inform user of DNSSEC validation status as well. + std::string dnssec_str; + if (dnssec_valid) + { + dnssec_str = tr("DNSSEC validation passed"); + } + else + { + dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); + } + std::stringstream prompt; + prompt << tr("For URL: ") << url + << ", " << dnssec_str << std::endl + << tr(" Monero Address = ") << addresses[0] + << std::endl + << tr("Is this OK? (Y/n) ") + ; + // prompt the user for confirmation given the dns query and dnssec status + std::string confirm_dns_ok = command_line::input_line(prompt.str()); + if (std::cin.eof()) + { + return {}; + } + if (!command_line::is_yes(confirm_dns_ok)) + { + std::cout << tr("you have cancelled the transfer request") << std::endl; + return {}; + } + return addresses[0]; +} + +bool get_account_address_from_str_or_url( + cryptonote::account_public_address& address + , bool& has_payment_id + , crypto::hash8& payment_id + , bool testnet + , const std::string& str_or_url + ) +{ + if (cryptonote::get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) + return true; + bool dnssec_valid; + std::string address_str = get_account_address_as_str_from_url(str_or_url, dnssec_valid); + return !address_str.empty() && + cryptonote::get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); +} + +} // namespace tools::dns_utils + } // namespace tools diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 2e87fb01f..5fe1d4775 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -25,9 +25,11 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once #include #include +#include "cryptonote_core/cryptonote_basic.h" namespace tools { @@ -155,4 +157,21 @@ private: DNSResolverData *m_data; }; // class DNSResolver +namespace dns_utils +{ + +std::string address_from_txt_record(const std::string& s); +std::vector addresses_from_url(const std::string& url, bool& dnssec_valid); + +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid); +bool get_account_address_from_str_or_url( + cryptonote::account_public_address& address + , bool& has_payment_id + , crypto::hash8& payment_id + , bool testnet + , const std::string& str_or_url + ); + +} // namespace tools::dns_utils + } // namespace tools diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index f07ef1616..7381dd06f 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "common/dns_utils.h" #include "daemon/command_parser_executor.h" namespace daemonize { @@ -238,17 +238,35 @@ bool t_command_parser_executor::start_mining(const std::vector& arg } cryptonote::account_public_address adr; + bool has_payment_id; + crypto::hash8 payment_id; bool testnet = false; - if(!cryptonote::get_account_address_from_str(adr, false, args.front())) + if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, args.front())) { - if(!cryptonote::get_account_address_from_str(adr, true, args.front())) + if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, args.front())) { - std::cout << "target account address has wrong format" << std::endl; - return true; + bool dnssec_valid; + std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid); + if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, address_str)) + { + if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, address_str)) + { + std::cout << "target account address has wrong format" << std::endl; + return true; + } + else + { + testnet = true; + } + } + } + else + { + testnet = true; } - testnet = true; - std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; } + if(testnet) + std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; uint64_t threads_count = 1; if(args.size() > 2) { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d03f3e7be..1f1770326 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -46,6 +46,7 @@ #include "common/i18n.h" #include "common/command_line.h" #include "common/util.h" +#include "common/dns_utils.h" #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" @@ -1877,75 +1878,6 @@ bool simple_wallet::rescan_spent(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id) -{ - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), str)) - { - // if treating as an address fails, try as url - bool dnssec_ok = false; - std::string url = str; - - // attempt to get address from dns query - auto addresses_from_dns = tools::wallet2::addresses_from_url(url, dnssec_ok); - - // for now, move on only if one address found - if (addresses_from_dns.size() == 1) - { - if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), addresses_from_dns[0])) - { - // if it was an address, prompt user for confirmation. - // inform user of DNSSEC validation status as well. - - std::string dnssec_str; - if (dnssec_ok) - { - dnssec_str = tr("DNSSEC validation passed"); - } - else - { - dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); - } - std::stringstream prompt; - prompt << tr("For URL: ") << url - << ", " << dnssec_str << std::endl - << tr(" Monero Address = ") << addresses_from_dns[0] - << std::endl - << tr("Is this OK? (Y/n) ") - ; - - // prompt the user for confirmation given the dns query and dnssec status - std::string confirm_dns_ok = command_line::input_line(prompt.str()); - if (std::cin.eof()) - { - return false; - } - if (!command_line::is_yes(confirm_dns_ok)) - { - fail_msg_writer() << tr("you have cancelled the transfer request"); - return false; - } - } - else - { - fail_msg_writer() << tr("failed to get a Monero address from: ") << url; - return false; - } - } - else if (addresses_from_dns.size() > 1) - { - fail_msg_writer() << tr("not yet supported: Multiple Monero addresses found for given URL: ") << url; - return false; - } - else - { - fail_msg_writer() << tr("wrong address: ") << url; - return false; - } - } - - return true; -} -//---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) { if (!try_connect_to_daemon()) @@ -2038,7 +1970,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectortestnet(), local_args[i])) return true; if (has_payment_id) @@ -2526,7 +2458,7 @@ bool simple_wallet::sweep_all(const std::vector &args_) bool has_payment_id; crypto::hash8 new_payment_id; cryptonote::account_public_address address; - if (!get_address_from_str(local_args[0], address, has_payment_id, new_payment_id)) + if (!tools::dns_utils::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0])) return true; if (has_payment_id) @@ -3074,7 +3006,7 @@ bool simple_wallet::check_tx_key(const std::vector &args_) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2])) + if(!tools::dns_utils::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2])) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -3614,7 +3546,7 @@ bool simple_wallet::verify(const std::vector &args) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), address_string)) + if(!tools::dns_utils::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), address_string)) { fail_msg_writer() << tr("failed to parse address"); return true; diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index c3e14a8cc..37662c998 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -162,7 +162,6 @@ namespace cryptonote bool accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message = std::string()); bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); - bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id); /*! * \brief Prints the seed with a nice message diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 4ee5ab8df..d29f3d375 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -32,6 +32,7 @@ #include "wallet_manager.h" #include "wallet.h" #include "common_defines.h" +#include "common/dns_utils.h" #include "net/http_client.h" #include @@ -354,7 +355,7 @@ double WalletManagerImpl::miningHashRate() const std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool &dnssec_valid) const { - std::vector addresses = tools::wallet2::addresses_from_url(address, dnssec_valid); + std::vector addresses = tools::dns_utils::addresses_from_url(address, dnssec_valid); if (addresses.empty()) return ""; return addresses.front(); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 221dd8e0b..7d76b110b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -51,7 +51,6 @@ using namespace epee; #include "cryptonote_protocol/blobdatatype.h" #include "mnemonics/electrum-words.h" #include "common/i18n.h" -#include "common/dns_utils.h" #include "common/util.h" #include "rapidjson/document.h" #include "rapidjson/writer.h" @@ -2854,74 +2853,7 @@ std::vector> split_amounts( return retVal; } } // anonymous namespace - -/** - * @brief gets a monero address from the TXT record of a DNS entry - * - * gets the monero address from the TXT record of the DNS entry associated - * with . If this lookup fails, or the TXT record does not contain an - * XMR address in the correct format, returns an empty string. - * will be set true or false according to whether or not the DNS query passes - * DNSSEC validation. - * - * @param url the url to look up - * @param dnssec_valid return-by-reference for DNSSEC status of query - * - * @return a monero address (as a string) or an empty string - */ -std::vector wallet2::addresses_from_url(const std::string& url, bool& dnssec_valid) -{ - std::vector addresses; - // get txt records - bool dnssec_available, dnssec_isvalid; - std::string oa_addr = tools::DNSResolver::instance().get_dns_format_from_oa_address(url); - auto records = tools::DNSResolver::instance().get_txt_record(oa_addr, dnssec_available, dnssec_isvalid); - - // TODO: update this to allow for conveying that dnssec was not available - if (dnssec_available && dnssec_isvalid) - { - dnssec_valid = true; - } - else dnssec_valid = false; - - // for each txt record, try to find a monero address in it. - for (auto& rec : records) - { - std::string addr = address_from_txt_record(rec); - if (addr.size()) - { - addresses.push_back(addr); - } - } - - return addresses; -} - //---------------------------------------------------------------------------------------------------- -// TODO: parse the string in a less stupid way, probably with regex -std::string wallet2::address_from_txt_record(const std::string& s) -{ - // make sure the txt record has "oa1:xmr" and find it - auto pos = s.find("oa1:xmr"); - - // search from there to find "recipient_address=" - pos = s.find("recipient_address=", pos); - - pos += 18; // move past "recipient_address=" - - // find the next semicolon - auto pos2 = s.find(";", pos); - if (pos2 != std::string::npos) - { - // length of address == 95, we can at least validate that much here - if (pos2 - pos == 95) - { - return s.substr(pos, 95); - } - } - return std::string(); -} - crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const { std::vector tx_extra_fields; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 54e26008b..b69c477e9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -503,10 +503,6 @@ namespace tools static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); - static std::vector addresses_from_url(const std::string& url, bool& dnssec_valid); - - static std::string address_from_txt_record(const std::string& s); - bool always_confirm_transfers() const { return m_always_confirm_transfers; } void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; } bool store_tx_info() const { return m_store_tx_info; } diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index ff163dab9..ad3aca6b4 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -31,6 +31,7 @@ #include "gtest/gtest.h" #include "wallet/wallet2.h" +#include "common/dns_utils.h" #include TEST(AddressFromTXT, Success) @@ -42,7 +43,7 @@ TEST(AddressFromTXT, Success) txtr += addr; txtr += ";"; - std::string res = tools::wallet2::address_from_txt_record(txtr); + std::string res = tools::dns_utils::address_from_txt_record(txtr); EXPECT_STREQ(addr.c_str(), res.c_str()); @@ -52,7 +53,7 @@ TEST(AddressFromTXT, Success) txtr2 += "more foobar"; - res = tools::wallet2::address_from_txt_record(txtr2); + res = tools::dns_utils::address_from_txt_record(txtr2); EXPECT_STREQ(addr.c_str(), res.c_str()); @@ -61,7 +62,7 @@ TEST(AddressFromTXT, Success) txtr3 += addr; txtr3 += "; foobar"; - res = tools::wallet2::address_from_txt_record(txtr3); + res = tools::dns_utils::address_from_txt_record(txtr3); EXPECT_STREQ(addr.c_str(), res.c_str()); } @@ -70,13 +71,13 @@ TEST(AddressFromTXT, Failure) { std::string txtr = "oa1:xmr recipient_address=not a real address"; - std::string res = tools::wallet2::address_from_txt_record(txtr); + std::string res = tools::dns_utils::address_from_txt_record(txtr); ASSERT_STREQ("", res.c_str()); txtr += ";"; - res = tools::wallet2::address_from_txt_record(txtr); + res = tools::dns_utils::address_from_txt_record(txtr); ASSERT_STREQ("", res.c_str()); } @@ -86,7 +87,7 @@ TEST(AddressFromURL, Success) bool dnssec_result = false; - std::vector addresses = tools::wallet2::addresses_from_url("donate.getmonero.org", dnssec_result); + std::vector addresses = tools::dns_utils::addresses_from_url("donate.getmonero.org", dnssec_result); EXPECT_EQ(1, addresses.size()); if (addresses.size() == 1) @@ -95,7 +96,7 @@ TEST(AddressFromURL, Success) } // OpenAlias address with an @ instead of first . - addresses = tools::wallet2::addresses_from_url("donate@getmonero.org", dnssec_result); + addresses = tools::dns_utils::addresses_from_url("donate@getmonero.org", dnssec_result); EXPECT_EQ(1, addresses.size()); if (addresses.size() == 1) { @@ -107,7 +108,7 @@ TEST(AddressFromURL, Failure) { bool dnssec_result = false; - std::vector addresses = tools::wallet2::addresses_from_url("example.invalid", dnssec_result); + std::vector addresses = tools::dns_utils::addresses_from_url("example.invalid", dnssec_result); // for a non-existing domain such as "example.invalid", the non-existence is proved with NSEC records ASSERT_TRUE(dnssec_result);