From 63741d82644944f236c93e9f44cb988a38df1993 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 11 Jun 2015 09:44:13 +0100 Subject: [PATCH] Integrated addresses (standard address plus payment id) --- src/cryptonote_config.h | 2 + src/cryptonote_core/account.cpp | 6 ++ src/cryptonote_core/account.h | 1 + src/cryptonote_core/cryptonote_basic_impl.cpp | 75 ++++++++++++++++++- src/cryptonote_core/cryptonote_basic_impl.h | 22 ++++++ src/simplewallet/simplewallet.cpp | 64 +++++++++++++++- src/simplewallet/simplewallet.h | 1 + src/wallet/wallet_rpc_server.cpp | 18 ++++- 8 files changed, 181 insertions(+), 8 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e5df844e..c6aa064c 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -121,6 +121,7 @@ namespace config std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; uint16_t const P2P_DEFAULT_PORT = 18080; uint16_t const RPC_DEFAULT_PORT = 18081; boost::uuids::uuid const NETWORK_ID = { { @@ -132,6 +133,7 @@ namespace config namespace testnet { uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53; + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54; uint16_t const P2P_DEFAULT_PORT = 28080; uint16_t const RPC_DEFAULT_PORT = 28081; boost::uuids::uuid const NETWORK_ID = { { diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index 55e36883..b68103dd 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -104,4 +104,10 @@ DISABLE_VS_WARNINGS(4244 4345) return get_account_address_as_str(testnet, m_keys.m_account_address); } //----------------------------------------------------------------- + std::string account_base::get_public_integrated_address_str(const crypto::hash &payment_id, bool testnet) + { + //TODO: change this code into base 58 + return get_account_integrated_address_as_str(testnet, m_keys.m_account_address, payment_id); + } + //----------------------------------------------------------------- } diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index dcfd9e8d..0f36029b 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -60,6 +60,7 @@ namespace cryptonote crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); const account_keys& get_keys() const; std::string get_public_address_str(bool testnet); + std::string get_public_integrated_address_str(const crypto::hash &payment_id, bool testnet); uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index f2d862e5..839c6ad1 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -44,6 +44,21 @@ using namespace epee; namespace cryptonote { + struct integrated_address { + account_public_address adr; + crypto::hash payment_id; + + BEGIN_SERIALIZE_OBJECT() + FIELD(adr) + FIELD(payment_id) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ @@ -106,6 +121,16 @@ namespace cryptonote { return summ; } + //------------------------------------------------------------------------------------ + uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl) + { + const unsigned char* pbuf = reinterpret_cast(&bl); + uint8_t summ = 0; + for(size_t i = 0; i!= sizeof(public_integrated_address_outer_blob)-1; i++) + summ += pbuf[i]; + + return summ; + } //----------------------------------------------------------------------- std::string get_account_address_as_str( bool testnet @@ -118,6 +143,21 @@ namespace cryptonote { return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr)); } //----------------------------------------------------------------------- + std::string get_account_integrated_address_as_str( + bool testnet + , account_public_address const & adr + , crypto::hash const & payment_id + ) + { + uint64_t integrated_address_prefix = testnet ? + config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + + integrated_address iadr = { + adr, payment_id + }; + return tools::base58::encode_addr(integrated_address_prefix, t_serializable_object_to_blob(iadr)); + } + //----------------------------------------------------------------------- bool is_coinbase(const transaction& tx) { if(tx.vin.size() != 1) @@ -129,14 +169,18 @@ namespace cryptonote { return true; } //----------------------------------------------------------------------- - bool get_account_address_from_str( + bool get_account_integrated_address_from_str( account_public_address& adr + , bool& has_payment_id + , crypto::hash& payment_id , bool testnet , std::string const & str ) { uint64_t address_prefix = testnet ? config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = testnet ? + config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; if (2 * sizeof(public_address_outer_blob) != str.size()) { @@ -148,18 +192,29 @@ namespace cryptonote { return false; } - if (address_prefix != prefix) + if (integrated_address_prefix == prefix) { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix); + has_payment_id = true; + } + else if (address_prefix == prefix) + { + has_payment_id = false; + } + else { + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix << " or " << integrated_address_prefix); return false; } - if (!::serialization::parse_binary(data, adr)) + integrated_address iadr; + if (!::serialization::parse_binary(data, iadr)) { LOG_PRINT_L1("Account public address keys can't be parsed"); return false; } + adr = iadr.adr; + payment_id = iadr.payment_id; + if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) { LOG_PRINT_L1("Failed to validate address keys"); @@ -196,10 +251,22 @@ namespace cryptonote { //we success adr = blob.m_address; + has_payment_id = false; } return true; } + //----------------------------------------------------------------------- + bool get_account_address_from_str( + account_public_address& adr + , bool testnet + , std::string const & str + ) + { + bool has_payment_id; + crypto::hash payment_id; + return get_account_integrated_address_from_str(adr, has_payment_id, payment_id, testnet, str); + } bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index 5c773c4e..87d6f102 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -56,6 +56,13 @@ namespace cryptonote { account_public_address m_address; uint8_t check_sum; }; + struct public_integrated_address_outer_blob + { + uint8_t m_ver; + account_public_address m_address; + crypto::hash payment_id; + uint8_t check_sum; + }; #pragma pack (pop) @@ -66,12 +73,27 @@ namespace cryptonote { size_t get_max_tx_size(); bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); + uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); std::string get_account_address_as_str( bool testnet , const account_public_address& adr ); + std::string get_account_integrated_address_as_str( + bool testnet + , const account_public_address& adr + , const crypto::hash& payment_id + ); + + bool get_account_integrated_address_from_str( + account_public_address& adr + , bool& has_payment_id + , crypto::hash& payment_id + , bool testnet + , const std::string& str + ); + bool get_account_address_from_str( account_public_address& adr , bool testnet diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3159e85d..c40e3f0a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -298,6 +298,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sweep_dust", boost::bind(&simple_wallet::sweep_dust, this, _1), "Send all dust outputs to the same address with mixin 0"); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); + m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::print_integrated_address, this, _1), "Show an integrated address for the current wallet public address and a payment id"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); m_cmd_binder.set_handler("save_watch_only", boost::bind(&simple_wallet::save_watch_only, this, _1), "Save watch only keys file"); m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), "Get viewkey"); @@ -1137,6 +1138,7 @@ bool simple_wallet::transfer(const std::vector &args_) } std::vector extra; + bool payment_id_seen = false; if (1 == local_args.size() % 2) { std::string payment_id_str = local_args.back(); @@ -1156,13 +1158,17 @@ bool simple_wallet::transfer(const std::vector &args_) fail_msg_writer() << "payment id has invalid format: \"" << payment_id_str << "\", expected 64-character string"; return true; } + payment_id_seen = true; } vector dsts; + crypto::hash payment_id = null_hash; for (size_t i = 0; i < local_args.size(); i += 2) { cryptonote::tx_destination_entry de; - if(!get_account_address_from_str(de.addr, m_wallet->testnet(), local_args[i])) + bool has_payment_id; + crypto::hash new_payment_id; + if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i])) { // if treating as an address fails, try as url bool dnssec_ok = false; @@ -1174,7 +1180,7 @@ bool simple_wallet::transfer(const std::vector &args_) // for now, move on only if one address found if (addresses_from_dns.size() == 1) { - if (get_account_address_from_str(de.addr, m_wallet->testnet(), addresses_from_dns[0])) + if (get_account_integrated_address_from_str(de.addr, has_payment_id, new_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. @@ -1221,6 +1227,26 @@ bool simple_wallet::transfer(const std::vector &args_) } } + if (has_payment_id) { + if (payment_id_seen && payment_id != new_payment_id) { + fail_msg_writer() << "A single transaction cannot use more than one payment id: " << local_args[i]; + return true; + } + + if (!payment_id_seen) + { + std::string extra_nonce; + set_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id); + bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + if(!r) + { + fail_msg_writer() << "Failed to set up payment id, though it was decoded correctly"; + return true; + } + payment_id = new_payment_id; + } + } + bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); if(!ok || 0 == de.amount) { @@ -1491,6 +1517,40 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::print_integrated_address(const std::vector &args/* = std::vector()*/) +{ + if (args.size() != 1) + { + fail_msg_writer() << "Missing payment id or address"; + return true; + } + crypto::hash payment_id; + if(tools::wallet2::parse_payment_id(args.back(), payment_id)) + { + success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet()); + return true; + } + else { + bool has_payment_id; + crypto::hash payment_id; + account_public_address addr; + if(get_account_integrated_address_from_str(addr, has_payment_id, payment_id, m_wallet->testnet(), args.back())) + { + if (has_payment_id) + { + success_msg_writer() << "Integrated address: account " << get_account_address_as_str(m_wallet->testnet(),addr) << ", payment id " << payment_id; + } + else + { + success_msg_writer() << "Standard address: account " << get_account_address_as_str(m_wallet->testnet(),addr); + } + return true; + } + } + fail_msg_writer() << "Failed to parse payment id or address"; + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::process_command(const std::vector &args) { return m_cmd_binder.process_command_vec(args); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index fa16fd38..bc100f50 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -108,6 +108,7 @@ namespace cryptonote std::vector dsts, size_t num_splits ); bool print_address(const std::vector &args = std::vector()); + bool print_integrated_address(const std::vector &args = std::vector()); bool save(const std::vector &args); bool save_watch_only(const std::vector &args); bool set_variable(const std::vector &args); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 9ef19f73..8bef8cde 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -117,12 +117,15 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er) + bool wallet_rpc_server::validate_transfer(const std::list destinations, std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er) { + crypto::hash integrated_payment_id = cryptonote::null_hash; for (auto it = destinations.begin(); it != destinations.end(); it++) { cryptonote::tx_destination_entry de; - if(!get_account_address_from_str(de.addr, m_wallet.testnet(), it->address)) + bool has_payment_id; + crypto::hash new_payment_id; + if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; @@ -130,6 +133,17 @@ namespace tools } de.amount = it->amount; dsts.push_back(de); + + if (has_payment_id) + { + if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "A single payment id is allowed per transaction"; + return false; + } + integrated_payment_id = new_payment_id; + } } if (!payment_id.empty())