aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_basic
diff options
context:
space:
mode:
authorkenshi84 <kenshi84@protonmail.ch>2017-02-19 11:42:10 +0900
committerkenshi84 <kenshi84@protonmail.ch>2017-10-07 13:06:21 +0900
commit53ad5a0f42174bca57e24485ef3d40e4b9cf5599 (patch)
treeafc13a3ee6a049ec78ac234e2d55ff46e992b457 /src/cryptonote_basic
parentMerge pull request #2548 (diff)
downloadmonero-53ad5a0f42174bca57e24485ef3d40e4b9cf5599.tar.xz
Subaddresses
Diffstat (limited to 'src/cryptonote_basic')
-rw-r--r--src/cryptonote_basic/account.cpp2
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h26
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp76
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.h30
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h5
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp138
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h20
-rw-r--r--src/cryptonote_basic/miner.cpp4
-rw-r--r--src/cryptonote_basic/subaddress_index.h102
-rw-r--r--src/cryptonote_basic/tx_extra.h14
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);