mirror of
https://github.com/monero-project/monero.git
synced 2025-07-27 22:25:27 -04:00
Merge pull request #8061
ea87b30
Add view tags to outputs to reduce wallet scanning time (j-berman)
This commit is contained in:
commit
96758a7d05
39 changed files with 1165 additions and 230 deletions
|
@ -74,6 +74,7 @@ namespace cryptonote
|
|||
crypto::hash hash;
|
||||
};
|
||||
|
||||
// outputs <= HF_VERSION_VIEW_TAGS
|
||||
struct txout_to_key
|
||||
{
|
||||
txout_to_key() { }
|
||||
|
@ -81,6 +82,19 @@ namespace cryptonote
|
|||
crypto::public_key key;
|
||||
};
|
||||
|
||||
// outputs >= HF_VERSION_VIEW_TAGS
|
||||
struct txout_to_tagged_key
|
||||
{
|
||||
txout_to_tagged_key() { }
|
||||
txout_to_tagged_key(const crypto::public_key &_key, const crypto::view_tag &_view_tag) : key(_key), view_tag(_view_tag) { }
|
||||
crypto::public_key key;
|
||||
crypto::view_tag view_tag; // optimization to reduce scanning time
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(key)
|
||||
FIELD(view_tag)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
/* inputs */
|
||||
|
||||
|
@ -137,7 +151,7 @@ namespace cryptonote
|
|||
|
||||
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key> txin_v;
|
||||
|
||||
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key> txout_target_v;
|
||||
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key, txout_to_tagged_key> txout_target_v;
|
||||
|
||||
//typedef std::pair<uint64_t, txout> out_t;
|
||||
struct tx_out
|
||||
|
@ -562,6 +576,7 @@ VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2);
|
|||
VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
|
||||
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
|
||||
|
||||
|
@ -572,6 +587,7 @@ VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key");
|
|||
VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(json_archive, cryptonote::block, "block");
|
||||
|
||||
|
@ -582,5 +598,6 @@ VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key");
|
|||
VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(debug_archive, cryptonote::block, "block");
|
||||
|
|
|
@ -71,7 +71,11 @@ namespace boost
|
|||
{
|
||||
a & reinterpret_cast<char (&)[sizeof(crypto::key_image)]>(x);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, crypto::view_tag &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & reinterpret_cast<char (&)[sizeof(crypto::view_tag)]>(x);
|
||||
}
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, crypto::signature &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
|
@ -102,6 +106,13 @@ namespace boost
|
|||
a & x.key;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::txout_to_tagged_key &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.key;
|
||||
a & x.view_tag;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::txout_to_scripthash &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
|
|
|
@ -155,12 +155,13 @@ namespace cryptonote
|
|||
}
|
||||
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
|
||||
{
|
||||
if (tx.vout[n].target.type() != typeid(txout_to_key))
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(tx.vout[n], output_public_key))
|
||||
{
|
||||
LOG_PRINT_L1("Unsupported output type in tx " << get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("Failed to get output public key for output " << n << " in tx " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
|
||||
rv.outPk[n].dest = rct::pk2rct(output_public_key);
|
||||
}
|
||||
|
||||
if (!base_only)
|
||||
|
@ -852,16 +853,16 @@ namespace cryptonote
|
|||
{
|
||||
for(const tx_out& out: tx.vout)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: "
|
||||
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
|
||||
<< ", in transaction id=" << get_transaction_hash(tx));
|
||||
crypto::public_key output_public_key;
|
||||
CHECK_AND_ASSERT_MES(get_output_public_key(out, output_public_key), false, "Failed to get output public key (output type: "
|
||||
<< out.target.type().name() << "), in transaction id=" << get_transaction_hash(tx));
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
|
||||
if(!check_key(boost::get<txout_to_key>(out.target).key))
|
||||
if(!check_key(output_public_key))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -905,6 +906,30 @@ namespace cryptonote
|
|||
return outputs_amount;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key)
|
||||
{
|
||||
// before HF_VERSION_VIEW_TAGS, outputs with public keys are of type txout_to_key
|
||||
// after HF_VERSION_VIEW_TAGS, outputs with public keys are of type txout_to_tagged_key
|
||||
if (out.target.type() == typeid(txout_to_key))
|
||||
output_public_key = boost::get< txout_to_key >(out.target).key;
|
||||
else if (out.target.type() == typeid(txout_to_tagged_key))
|
||||
output_public_key = boost::get< txout_to_tagged_key >(out.target).key;
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Unexpected output target type found: " << out.target.type().name());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out)
|
||||
{
|
||||
return out.target.type() == typeid(txout_to_tagged_key)
|
||||
? boost::optional<crypto::view_tag>(boost::get< txout_to_tagged_key >(out.target).view_tag)
|
||||
: boost::optional<crypto::view_tag>();
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
std::string short_hash_str(const crypto::hash& h)
|
||||
{
|
||||
std::string res = string_tools::pod_to_hex(h);
|
||||
|
@ -914,45 +939,126 @@ 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, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index)
|
||||
void set_tx_out(const uint64_t amount, const crypto::public_key& output_public_key, const bool use_view_tags, const crypto::view_tag& view_tag, tx_out& out)
|
||||
{
|
||||
out.amount = amount;
|
||||
if (use_view_tags)
|
||||
{
|
||||
txout_to_tagged_key ttk;
|
||||
ttk.key = output_public_key;
|
||||
ttk.view_tag = view_tag;
|
||||
out.target = ttk;
|
||||
}
|
||||
else
|
||||
{
|
||||
txout_to_key tk;
|
||||
tk.key = output_public_key;
|
||||
out.target = tk;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool check_output_types(const transaction& tx, const uint8_t hf_version)
|
||||
{
|
||||
for (const auto &o: tx.vout)
|
||||
{
|
||||
if (hf_version > HF_VERSION_VIEW_TAGS)
|
||||
{
|
||||
// from v15, require outputs have view tags
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_tagged_key), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_tagged_key in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
else if (hf_version < HF_VERSION_VIEW_TAGS)
|
||||
{
|
||||
// require outputs to be of type txout_to_key
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_key in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
else //(hf_version == HF_VERSION_VIEW_TAGS)
|
||||
{
|
||||
// require outputs be of type txout_to_key OR txout_to_tagged_key
|
||||
// to allow grace period before requiring all to be txout_to_tagged_key
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key) || o.target.type() == typeid(txout_to_tagged_key), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_key or txout_to_tagged_key in transaction id=" << get_transaction_hash(tx));
|
||||
|
||||
// require all outputs in a tx be of the same type
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == tx.vout[0].target.type(), false, "non-matching variant types: "
|
||||
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
|
||||
<< "expected matching variant types in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index)
|
||||
{
|
||||
// If there is no view tag to check, the output can possibly belong to the account.
|
||||
// Will need to derive the output pub key to be certain whether or not the output belongs to the account.
|
||||
if (!view_tag_opt)
|
||||
return true;
|
||||
|
||||
crypto::view_tag view_tag = *view_tag_opt;
|
||||
|
||||
// If the output's view tag does *not* match the derived view tag, the output should not belong to the account.
|
||||
// Therefore can fail out early to avoid expensive crypto ops needlessly deriving output public key to
|
||||
// determine if output belongs to the account.
|
||||
crypto::view_tag derived_view_tag;
|
||||
crypto::derive_view_tag(derivation, output_index, derived_view_tag);
|
||||
return view_tag == derived_view_tag;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index, const boost::optional<crypto::view_tag>& view_tag_opt)
|
||||
{
|
||||
crypto::key_derivation derivation;
|
||||
bool r = acc.get_device().generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
|
||||
crypto::public_key pk;
|
||||
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
|
||||
if (pk == out_key.key)
|
||||
return true;
|
||||
if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
|
||||
{
|
||||
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
|
||||
if (pk == output_public_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");
|
||||
r = acc.get_device().generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
|
||||
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
|
||||
return pk == out_key.key;
|
||||
if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
|
||||
{
|
||||
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
|
||||
return pk == output_public_key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
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, hw::device &hwdev)
|
||||
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, hw::device &hwdev, const boost::optional<crypto::view_tag>& view_tag_opt)
|
||||
{
|
||||
// try the shared tx pubkey
|
||||
crypto::public_key subaddress_spendkey;
|
||||
hwdev.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 };
|
||||
if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
|
||||
{
|
||||
hwdev.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");
|
||||
hwdev.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] };
|
||||
if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index))
|
||||
{
|
||||
hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey);
|
||||
auto found = subaddresses.find(subaddress_spendkey);
|
||||
if (found != subaddresses.end())
|
||||
return subaddress_receive_info{ found->second, additional_derivations[output_index] };
|
||||
}
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
@ -973,8 +1079,9 @@ namespace cryptonote
|
|||
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, additional_tx_pub_keys, i))
|
||||
crypto::public_key output_public_key;
|
||||
CHECK_AND_ASSERT_MES(get_output_public_key(o, output_public_key), false, "unable to get output public key from transaction out" );
|
||||
if(is_out_to_acc(acc, output_public_key, tx_pub_key, additional_tx_pub_keys, i, get_output_view_tag(o)))
|
||||
{
|
||||
outs.push_back(i);
|
||||
money_transfered += o.amount;
|
||||
|
|
|
@ -89,13 +89,16 @@ namespace cryptonote
|
|||
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, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t output_index);
|
||||
void set_tx_out(const uint64_t amount, const crypto::public_key& output_public_key, const bool use_view_tags, const crypto::view_tag& view_tag, tx_out& out);
|
||||
bool check_output_types(const transaction& tx, const uint8_t hf_version);
|
||||
bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index);
|
||||
bool is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t output_index, const boost::optional<crypto::view_tag>& view_tag_opt = boost::optional<crypto::view_tag>());
|
||||
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, hw::device &hwdev);
|
||||
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, hw::device &hwdev, const boost::optional<crypto::view_tag>& view_tag_opt = boost::optional<crypto::view_tag>());
|
||||
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);
|
||||
|
@ -126,6 +129,8 @@ namespace cryptonote
|
|||
bool parse_and_validate_block_from_blob(const blobdata_ref& b_blob, block& b, crypto::hash &block_hash);
|
||||
bool get_inputs_money_amount(const transaction& tx, uint64_t& money);
|
||||
uint64_t get_outs_money_amount(const transaction& tx);
|
||||
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key);
|
||||
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out);
|
||||
bool check_inputs_types_supported(const transaction& tx);
|
||||
bool check_outs_valid(const transaction& tx);
|
||||
bool parse_amount(uint64_t& amount, const std::string& str_amount);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue