diff options
author | kenshi84 <kenshi84@protonmail.ch> | 2017-02-19 11:42:10 +0900 |
---|---|---|
committer | kenshi84 <kenshi84@protonmail.ch> | 2017-10-07 13:06:21 +0900 |
commit | 53ad5a0f42174bca57e24485ef3d40e4b9cf5599 (patch) | |
tree | afc13a3ee6a049ec78ac234e2d55ff46e992b457 /src/cryptonote_basic | |
parent | Merge pull request #2548 (diff) | |
download | monero-53ad5a0f42174bca57e24485ef3d40e4b9cf5599.tar.xz |
Subaddresses
Diffstat (limited to 'src/cryptonote_basic')
-rw-r--r-- | src/cryptonote_basic/account.cpp | 2 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_basic.h | 26 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_basic_impl.cpp | 76 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_basic_impl.h | 30 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_boost_serialization.h | 5 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_format_utils.cpp | 138 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_format_utils.h | 20 | ||||
-rw-r--r-- | src/cryptonote_basic/miner.cpp | 4 | ||||
-rw-r--r-- | src/cryptonote_basic/subaddress_index.h | 102 | ||||
-rw-r--r-- | src/cryptonote_basic/tx_extra.h | 14 |
10 files changed, 333 insertions, 84 deletions
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index dd875402f..fb832d88e 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -131,7 +131,7 @@ DISABLE_VS_WARNINGS(4244 4345) std::string account_base::get_public_address_str(bool testnet) const { //TODO: change this code into base 58 - return get_account_address_as_str(testnet, m_keys.m_account_address); + return get_account_address_as_str(testnet, false, m_keys.m_account_address); } //----------------------------------------------------------------- std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index eb03d33b9..89dda8c3d 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -411,6 +411,17 @@ namespace cryptonote KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key) END_KV_SERIALIZE_MAP() + + bool operator==(const account_public_address& rhs) const + { + return m_spend_public_key == rhs.m_spend_public_key && + m_view_public_key == rhs.m_view_public_key; + } + + bool operator!=(const account_public_address& rhs) const + { + return !(*this == rhs); + } }; struct keypair @@ -429,6 +440,21 @@ namespace cryptonote } +namespace std { + template <> + struct hash<cryptonote::account_public_address> + { + std::size_t operator()(const cryptonote::account_public_address& addr) const + { + // https://stackoverflow.com/a/17017281 + size_t res = 17; + res = res * 31 + hash<crypto::public_key>()(addr.m_spend_public_key); + res = res * 31 + hash<crypto::public_key>()(addr.m_view_public_key); + return res; + } + }; +} + BLOB_SERIALIZER(cryptonote::txout_to_key); BLOB_SERIALIZER(cryptonote::txout_to_scripthash); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index a59f96956..1183fda06 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -158,11 +158,13 @@ namespace cryptonote { //----------------------------------------------------------------------- std::string get_account_address_as_str( bool testnet + , bool subaddress , account_public_address const & adr ) { uint64_t address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + (subaddress ? config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : + (subaddress ? config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr)); } @@ -173,8 +175,7 @@ namespace cryptonote { , crypto::hash8 const & payment_id ) { - uint64_t integrated_address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + 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 @@ -193,10 +194,8 @@ namespace cryptonote { return true; } //----------------------------------------------------------------------- - bool get_account_integrated_address_from_str( - account_public_address& adr - , bool& has_payment_id - , crypto::hash8& payment_id + bool get_account_address_from_str( + address_parse_info& info , bool testnet , std::string const & str ) @@ -205,6 +204,8 @@ namespace cryptonote { 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; + uint64_t subaddress_prefix = testnet ? + config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; if (2 * sizeof(public_address_outer_blob) != str.size()) { @@ -218,18 +219,27 @@ namespace cryptonote { if (integrated_address_prefix == prefix) { - has_payment_id = true; + info.is_subaddress = false; + info.has_payment_id = true; } else if (address_prefix == prefix) { - has_payment_id = false; + info.is_subaddress = false; + info.has_payment_id = false; + } + else if (subaddress_prefix == prefix) + { + info.is_subaddress = true; + info.has_payment_id = false; } else { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix << " or " << integrated_address_prefix); + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix + << " or " << integrated_address_prefix + << " or " << subaddress_prefix); return false; } - if (has_payment_id) + if (info.has_payment_id) { integrated_address iadr; if (!::serialization::parse_binary(data, iadr)) @@ -237,19 +247,19 @@ namespace cryptonote { LOG_PRINT_L1("Account public address keys can't be parsed"); return false; } - adr = iadr.adr; - payment_id = iadr.payment_id; + info.address = iadr.adr; + info.payment_id = iadr.payment_id; } else { - if (!::serialization::parse_binary(data, adr)) + if (!::serialization::parse_binary(data, info.address)) { LOG_PRINT_L1("Account public address keys can't be parsed"); return false; } } - if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) + if (!crypto::check_key(info.address.m_spend_public_key) || !crypto::check_key(info.address.m_view_public_key)) { LOG_PRINT_L1("Failed to validate address keys"); return false; @@ -284,51 +294,27 @@ namespace cryptonote { } //we success - adr = blob.m_address; - has_payment_id = false; + info.address = blob.m_address; + info.is_subaddress = false; + info.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::hash8 payment_id; - return get_account_integrated_address_from_str(adr, has_payment_id, payment_id, testnet, str); - } //-------------------------------------------------------------------------------- bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool& has_payment_id - , crypto::hash8& payment_id + address_parse_info& info , bool testnet , const std::string& str_or_url , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm ) { - if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) + if (get_account_address_from_str(info, testnet, str_or_url)) return true; bool dnssec_valid; std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, dns_confirm); return !address_str.empty() && - get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); - } - //-------------------------------------------------------------------------------- - bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool testnet - , const std::string& str_or_url - , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm - ) - { - bool has_payment_id; - crypto::hash8 payment_id; - return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, dns_confirm); + get_account_address_from_str(info, testnet, address_str); } //-------------------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 5523846d6..08d966fed 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -75,6 +75,14 @@ namespace cryptonote { } } + struct address_parse_info + { + account_public_address address; + bool is_subaddress; + bool has_payment_id; + crypto::hash8 payment_id; + }; + /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ @@ -87,6 +95,7 @@ namespace cryptonote { std::string get_account_address_as_str( bool testnet + , bool subaddress , const account_public_address& adr ); @@ -96,31 +105,14 @@ namespace cryptonote { , const crypto::hash8& payment_id ); - bool get_account_integrated_address_from_str( - account_public_address& adr - , bool& has_payment_id - , crypto::hash8& payment_id - , bool testnet - , const std::string& str - ); - bool get_account_address_from_str( - account_public_address& adr + address_parse_info& info , bool testnet , const std::string& str ); 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 - , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address - ); - - bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address + address_parse_info& info , bool testnet , const std::string& str_or_url , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 6e4ac9b72..a67fa0ae7 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -83,6 +83,11 @@ namespace boost { a & reinterpret_cast<char (&)[sizeof(crypto::hash)]>(x); } + template <class Archive> + inline void serialize(Archive &a, crypto::hash8 &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::hash8)]>(x); + } template <class Archive> inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index fc979f288..6f171f227 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -129,16 +129,69 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index) + { + const char prefix[] = "SubAddr"; + char data[sizeof(prefix) + sizeof(crypto::secret_key) + sizeof(subaddress_index)]; + memcpy(data, prefix, sizeof(prefix)); + memcpy(data + sizeof(prefix), &a, sizeof(crypto::secret_key)); + memcpy(data + sizeof(prefix) + sizeof(crypto::secret_key), &index, sizeof(subaddress_index)); + crypto::secret_key m; + crypto::hash_to_scalar(data, sizeof(data), m); + return m; + } + //--------------------------------------------------------------- + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + std::vector<crypto::key_derivation> additional_recv_derivations; + for (size_t i = 0; i < additional_tx_public_keys.size(); ++i) + { + crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation); + r = crypto::generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")"); + additional_recv_derivations.push_back(additional_recv_derivation); + } + + boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index); + CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address"); + + return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki); + } + //--------------------------------------------------------------- + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki) + { + if (ack.m_spend_secret_key == crypto::null_skey) + { + // for watch-only wallet, simply copy the known output pubkey + in_ephemeral.pub = out_key; + in_ephemeral.sec = crypto::null_skey; + } + else + { + // derive secret key with subaddress - step 1: original CN derivation + crypto::secret_key scalar_step1; + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b + + // step 2: add Hs(a || index_major || index_minor) + crypto::secret_key scalar_step2; + if (received_index.is_zero()) + { + scalar_step2 = scalar_step1; // treat index=(0,0) as a special case representing the main address + } + else + { + crypto::secret_key m = get_subaddress_secret_key(ack.m_view_secret_key, received_index); + sc_add((unsigned char*)&scalar_step2, (unsigned char*)&scalar_step1, (unsigned char*)&m); + } - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + in_ephemeral.sec = scalar_step2; + crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, false, "key image helper precomp: given output pubkey doesn't match the derived one"); + } crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); return true; @@ -277,6 +330,40 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra) + { + // parse + std::vector<tx_extra_field> tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); + // find corresponding field + tx_extra_additional_pub_keys additional_pub_keys; + if(!find_tx_extra_field_by_type(tx_extra_fields, additional_pub_keys)) + return {}; + return additional_pub_keys.data; + } + //--------------------------------------------------------------- + std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx) + { + return get_additional_tx_pub_keys_from_extra(tx.extra); + } + //--------------------------------------------------------------- + bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys) + { + // convert to variant + tx_extra_field field = tx_extra_additional_pub_keys{ additional_pub_keys }; + // serialize + std::ostringstream oss; + binary_archive<true> ar(oss); + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra additional tx pub keys"); + // append + std::string tx_extra_str = oss.str(); + size_t pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + tx_extra_str.size()); + memcpy(&tx_extra[pos], tx_extra_str.data(), tx_extra_str.size()); + return true; + } + //--------------------------------------------------------------- bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce) { CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); @@ -480,20 +567,43 @@ namespace cryptonote return res; } //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index) { crypto::key_derivation derivation; generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); crypto::public_key pk; derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); - return pk == out_key.key; + if (pk == out_key.key) + return true; + // try additional tx pubkeys if available + if (!additional_tx_pub_keys.empty()) + { + CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys"); + generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation); + derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + return pk == out_key.key; + } + return false; } //--------------------------------------------------------------- - bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) + boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index) { - crypto::public_key pk; - derive_public_key(derivation, output_index, spend_public_key, pk); - return pk == out_key.key; + // try the shared tx pubkey + crypto::public_key subaddress_spendkey; + derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey); + auto found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, derivation }; + // try additional tx pubkeys if available + if (!additional_derivations.empty()) + { + CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); + derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey); + found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, additional_derivations[output_index] }; + } + return boost::none; } //--------------------------------------------------------------- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered) @@ -501,17 +611,19 @@ namespace cryptonote crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); if(null_pkey == tx_pub_key) return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); + std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); + return lookup_acc_outs(acc, tx, tx_pub_key, additional_tx_pub_keys, outs, money_transfered); } //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered) + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, std::vector<size_t>& outs, uint64_t& money_transfered) { + CHECK_AND_ASSERT_MES(additional_tx_pub_keys.empty() || additional_tx_pub_keys.size() == tx.vout.size(), false, "wrong number of additional pubkeys" ); money_transfered = 0; size_t i = 0; for(const tx_out& o: tx.vout) { CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); - if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i)) + if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, additional_tx_pub_keys, i)) { outs.push_back(i); money_transfered += o.amount; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 00080fb98..078c8b8a0 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -32,9 +32,11 @@ #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_basic_impl.h" #include "account.h" +#include "subaddress_index.h" #include "include_base_utils.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include <unordered_map> namespace cryptonote { @@ -63,19 +65,29 @@ namespace cryptonote crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0); crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0); bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); + std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra); + std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx); + bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys); bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id); bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id); - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); - bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index); - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered); + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t output_index); + struct subaddress_receive_info + { + subaddress_index index; + crypto::key_derivation derivation; + }; + boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, std::vector<size_t>& outs, uint64_t& money_transfered); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered); bool get_tx_fee(const transaction& tx, uint64_t & fee); uint64_t get_tx_fee(const transaction& tx); - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index); + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki); void get_blob_hash(const blobdata& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); std::string short_hash_str(const crypto::hash& h); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index b620e3426..8f53c2d3c 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -228,11 +228,13 @@ namespace cryptonote if(command_line::has_arg(vm, arg_start_mining)) { - if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) + address_parse_info info; + if(!cryptonote::get_account_address_from_str(info, testnet, command_line::get_arg(vm, arg_start_mining)) || info.is_subaddress) { LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); return false; } + m_mine_address = info.address; m_threads_total = 1; m_do_mining = true; if(command_line::has_arg(vm, arg_mining_threads)) diff --git a/src/cryptonote_basic/subaddress_index.h b/src/cryptonote_basic/subaddress_index.h new file mode 100644 index 000000000..07d13c503 --- /dev/null +++ b/src/cryptonote_basic/subaddress_index.h @@ -0,0 +1,102 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// 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 "serialization/keyvalue_serialization.h" +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/version.hpp> +#include <ostream> + +namespace cryptonote +{ + struct subaddress_index + { + uint32_t major; + uint32_t minor; + bool operator==(const subaddress_index& rhs) const { return !memcmp(this, &rhs, sizeof(subaddress_index)); } + bool operator!=(const subaddress_index& rhs) const { return !(*this == rhs); } + bool is_zero() const { return major == 0 && minor == 0; } + + BEGIN_SERIALIZE_OBJECT() + FIELD(major) + FIELD(minor) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(major) + KV_SERIALIZE(minor) + END_KV_SERIALIZE_MAP() + }; +} + +namespace cryptonote { + inline std::ostream& operator<<(std::ostream& out, const cryptonote::subaddress_index& subaddr_index) + { + return out << subaddr_index.major << '/' << subaddr_index.minor; + } +} + +namespace std +{ + template <> + struct hash<cryptonote::subaddress_index> + { + size_t operator()(const cryptonote::subaddress_index& index ) const + { + size_t res; + if (sizeof(size_t) == 8) + { + res = ((uint64_t)index.major << 32) | index.minor; + } + else + { + // https://stackoverflow.com/a/17017281 + res = 17; + res = res * 31 + hash<uint32_t>()(index.major); + res = res * 31 + hash<uint32_t>()(index.minor); + } + return res; + } + }; +} + +BOOST_CLASS_VERSION(cryptonote::subaddress_index, 0) + +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, cryptonote::subaddress_index &x, const boost::serialization::version_type ver) + { + a & x.major; + a & x.minor; + } + } +} diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 5a6c3176d..e12828a9f 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -38,6 +38,7 @@ #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 +#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -159,6 +160,16 @@ namespace cryptonote } }; + // per-output additional tx pubkey for multi-destination transfers involving at least one subaddress + struct tx_extra_additional_pub_keys + { + std::vector<crypto::public_key> data; + + BEGIN_SERIALIZE() + FIELD(data) + END_SERIALIZE() + }; + struct tx_extra_mysterious_minergate { std::string data; @@ -172,11 +183,12 @@ namespace cryptonote // varint tag; // varint size; // varint data[]; - typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_mysterious_minergate> tx_extra_field; + typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS); VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG); |