Merge pull request #8061
ea87b30
Add view tags to outputs to reduce wallet scanning time (j-berman)
This commit is contained in:
commit
96758a7d05
@ -1045,8 +1045,9 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
|
||||
CURSOR(output_txs)
|
||||
CURSOR(output_amounts)
|
||||
|
||||
if (tx_output.target.type() != typeid(txout_to_key))
|
||||
throw0(DB_ERROR("Wrong output type: expected txout_to_key"));
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(tx_output, output_public_key))
|
||||
throw0(DB_ERROR("Could not get an output public key from a tx output."));
|
||||
if (tx_output.amount == 0 && !commitment)
|
||||
throw0(DB_ERROR("RCT output without commitment"));
|
||||
|
||||
@ -1074,7 +1075,7 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
|
||||
else
|
||||
ok.amount_index = 0;
|
||||
ok.output_id = m_num_outputs;
|
||||
ok.data.pubkey = boost::get < txout_to_key > (tx_output.target).key;
|
||||
ok.data.pubkey = output_public_key;
|
||||
ok.data.unlock_time = unlock_time;
|
||||
ok.data.height = m_height;
|
||||
if (tx_output.amount == 0)
|
||||
|
@ -749,4 +749,28 @@ POP_WARNINGS
|
||||
sc_sub(&h, &h, &sum);
|
||||
return sc_isnonzero(&h) == 0;
|
||||
}
|
||||
|
||||
void crypto_ops::derive_view_tag(const key_derivation &derivation, size_t output_index, view_tag &view_tag) {
|
||||
#pragma pack(push, 1)
|
||||
struct {
|
||||
char salt[8]; // view tag domain-separator
|
||||
key_derivation derivation;
|
||||
char output_index[(sizeof(size_t) * 8 + 6) / 7];
|
||||
} buf;
|
||||
#pragma pack(pop)
|
||||
|
||||
char *end = buf.output_index;
|
||||
memcpy(buf.salt, "view_tag", 8); // leave off null terminator
|
||||
buf.derivation = derivation;
|
||||
tools::write_varint(end, output_index);
|
||||
assert(end <= buf.output_index + sizeof buf.output_index);
|
||||
|
||||
// view_tag_full = H[salt|derivation|output_index]
|
||||
hash view_tag_full;
|
||||
cn_fast_hash(&buf, end - reinterpret_cast<char *>(&buf), view_tag_full);
|
||||
|
||||
// only need a slice of view_tag_full to realize optimal perf/space efficiency
|
||||
static_assert(sizeof(crypto::view_tag) <= sizeof(view_tag_full), "view tag should not be larger than hash result");
|
||||
memcpy(&view_tag, &view_tag_full, sizeof(crypto::view_tag));
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,10 @@ namespace crypto {
|
||||
ec_scalar c, r;
|
||||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
POD_CLASS view_tag {
|
||||
char data;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void hash_to_scalar(const void *data, size_t length, ec_scalar &res);
|
||||
@ -107,7 +111,7 @@ namespace crypto {
|
||||
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
|
||||
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
|
||||
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
|
||||
sizeof(signature) == 64, "Invalid structure size");
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
@ -151,6 +155,8 @@ namespace crypto {
|
||||
const public_key *const *, std::size_t, const signature *);
|
||||
friend bool check_ring_signature(const hash &, const key_image &,
|
||||
const public_key *const *, std::size_t, const signature *);
|
||||
static void derive_view_tag(const key_derivation &, std::size_t, view_tag &);
|
||||
friend void derive_view_tag(const key_derivation &, std::size_t, view_tag &);
|
||||
};
|
||||
|
||||
void generate_random_bytes_thread_safe(size_t N, uint8_t *bytes);
|
||||
@ -297,6 +303,14 @@ namespace crypto {
|
||||
return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig);
|
||||
}
|
||||
|
||||
/* Derive a 1-byte view tag from the sender-receiver shared secret to reduce scanning time.
|
||||
* When scanning outputs that were not sent to the user, checking the view tag for a match removes the need to proceed with expensive EC operations
|
||||
* for an expected 99.6% of outputs (expected false positive rate = 1/2^8 = 1/256 = 0.4% = 100% - 99.6%).
|
||||
*/
|
||||
inline void derive_view_tag(const key_derivation &derivation, std::size_t output_index, view_tag &vt) {
|
||||
crypto_ops::derive_view_tag(derivation, output_index, vt);
|
||||
}
|
||||
|
||||
inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
|
||||
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
|
||||
}
|
||||
@ -312,6 +326,9 @@ namespace crypto {
|
||||
inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
|
||||
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
|
||||
}
|
||||
inline std::ostream &operator <<(std::ostream &o, const crypto::view_tag &v) {
|
||||
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
|
||||
}
|
||||
|
||||
const extern crypto::public_key null_pkey;
|
||||
const extern crypto::secret_key null_skey;
|
||||
@ -325,3 +342,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(secret_key)
|
||||
CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
|
||||
CRYPTO_MAKE_HASHABLE(key_image)
|
||||
CRYPTO_MAKE_COMPARABLE(signature)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
|
@ -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);
|
||||
|
@ -183,6 +183,7 @@
|
||||
#define HF_VERSION_CLSAG 13
|
||||
#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 13
|
||||
#define HF_VERSION_BULLETPROOF_PLUS 15
|
||||
#define HF_VERSION_VIEW_TAGS 15
|
||||
#define HF_VERSION_2021_SCALING 15
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
|
@ -1341,6 +1341,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||
// one input, of type txin_gen, with height set to the block's height
|
||||
// correct miner tx unlock time
|
||||
// a non-overflowing tx amount (dubious necessity on this check)
|
||||
// valid output types
|
||||
bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
@ -1369,6 +1370,8 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.miner_tx, hf_version), false, "miner transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@ -3044,12 +3047,14 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
// from v4, forbid invalid pubkeys
|
||||
if (hf_version >= 4) {
|
||||
for (const auto &o: tx.vout) {
|
||||
if (o.target.type() == typeid(txout_to_key)) {
|
||||
const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target);
|
||||
if (!crypto::check_key(out_to_key.key)) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(o, output_public_key)) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
if (!crypto::check_key(output_public_key)) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3166,6 +3171,13 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
}
|
||||
}
|
||||
|
||||
// from v15, require view tags on outputs
|
||||
if (!check_output_types(tx, hf_version))
|
||||
{
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@ -3977,9 +3989,11 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
|
||||
}
|
||||
|
||||
// The original code includes a check for the output corresponding to this input
|
||||
// to be a txout_to_key. This is removed, as the database does not store this info,
|
||||
// but only txout_to_key outputs are stored in the DB in the first place, done in
|
||||
// Blockchain*::add_output
|
||||
// to be a txout_to_key. This is removed, as the database does not store this info.
|
||||
// Only txout_to_key (and since HF_VERSION_VIEW_TAGS, txout_to_tagged_key)
|
||||
// outputs are stored in the DB in the first place, done in Blockchain*::add_output.
|
||||
// Additional type checks on outputs were also added via cryptonote::check_output_types
|
||||
// and cryptonote::get_output_public_key (see Blockchain::check_tx_outputs).
|
||||
|
||||
m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment}));
|
||||
return true;
|
||||
|
@ -1177,7 +1177,8 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_tx_inputs_ring_members_diff(tx))
|
||||
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
if (!check_tx_inputs_ring_members_diff(tx, hf_version))
|
||||
{
|
||||
MERROR_VER("tx uses duplicate ring members");
|
||||
return false;
|
||||
@ -1189,6 +1190,12 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_output_types(tx, hf_version))
|
||||
{
|
||||
MERROR_VER("tx does not use valid output type(s)");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
@ -1295,10 +1302,9 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const
|
||||
bool core::check_tx_inputs_ring_members_diff(const transaction& tx, const uint8_t hf_version) const
|
||||
{
|
||||
const uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
if (version >= 6)
|
||||
if (hf_version >= 6)
|
||||
{
|
||||
for(const auto& in: tx.vin)
|
||||
{
|
||||
|
@ -1012,10 +1012,11 @@ namespace cryptonote
|
||||
* @brief verify that each ring uses distinct members
|
||||
*
|
||||
* @param tx the transaction to check
|
||||
* @param hf_version the hard fork version rules to use
|
||||
*
|
||||
* @return false if any ring uses duplicate members, true otherwise
|
||||
*/
|
||||
bool check_tx_inputs_ring_members_diff(const transaction& tx) const;
|
||||
bool check_tx_inputs_ring_members_diff(const transaction& tx, const uint8_t hf_version) const;
|
||||
|
||||
/**
|
||||
* @brief verify that each input key image in a transaction is in
|
||||
|
@ -149,12 +149,17 @@ namespace cryptonote
|
||||
r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
|
||||
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
uint64_t amount = out_amounts[no];
|
||||
summary_amounts += amount;
|
||||
|
||||
bool use_view_tags = hard_fork_version >= HF_VERSION_VIEW_TAGS;
|
||||
crypto::view_tag view_tag;
|
||||
if (use_view_tags)
|
||||
crypto::derive_view_tag(derivation, no, view_tag);
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = out_amounts[no];
|
||||
out.target = tk;
|
||||
cryptonote::set_tx_out(amount, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
@ -198,7 +203,7 @@ namespace cryptonote
|
||||
return addr.m_view_public_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs)
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs, bool use_view_tags)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
|
||||
@ -406,17 +411,16 @@ namespace cryptonote
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
|
||||
hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
|
||||
dst_entr, change_addr, output_index,
|
||||
need_additional_txkeys, additional_tx_keys,
|
||||
additional_tx_public_keys, amount_keys, out_eph_public_key);
|
||||
additional_tx_public_keys, amount_keys, out_eph_public_key,
|
||||
use_view_tags, view_tag);
|
||||
|
||||
tx_out out;
|
||||
out.amount = dst_entr.amount;
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
out.target = tk;
|
||||
cryptonote::set_tx_out(dst_entr.amount, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
tx.vout.push_back(out);
|
||||
output_index++;
|
||||
summary_outs_money += dst_entr.amount;
|
||||
@ -546,7 +550,9 @@ namespace cryptonote
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
|
||||
crypto::public_key output_public_key;
|
||||
get_output_public_key(tx.vout[i], output_public_key);
|
||||
destinations.push_back(rct::pk2rct(output_public_key));
|
||||
outamounts.push_back(tx.vout[i].amount);
|
||||
amount_out += tx.vout[i].amount;
|
||||
}
|
||||
@ -607,7 +613,7 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout)
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool use_view_tags)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
hwdev.open_tx(tx_key);
|
||||
@ -627,7 +633,8 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout);
|
||||
bool shuffle_outs = true;
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout, shuffle_outs, use_view_tags);
|
||||
hwdev.close_tx();
|
||||
return r;
|
||||
} catch(...) {
|
||||
@ -643,7 +650,7 @@ namespace cryptonote
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL);
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL, false);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_genesis_block(
|
||||
|
@ -119,21 +119,23 @@ namespace cryptonote
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool use_view_tags = false);
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) ;
|
||||
crypto::public_key &out_eph_public_key,
|
||||
const bool use_view_tags, crypto::view_tag &view_tag) ;
|
||||
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) ;
|
||||
crypto::public_key &out_eph_public_key,
|
||||
const bool use_view_tags, crypto::view_tag &view_tag) ;
|
||||
|
||||
bool generate_genesis_block(
|
||||
block& bl
|
||||
|
@ -222,7 +222,8 @@ namespace hw {
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) = 0;
|
||||
crypto::public_key &out_eph_public_key,
|
||||
const bool use_view_tags, crypto::view_tag &view_tag) = 0;
|
||||
|
||||
virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0;
|
||||
virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0;
|
||||
|
@ -263,6 +263,11 @@ namespace hw {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_default::derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) {
|
||||
crypto::derive_view_tag(derivation, output_index, view_tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_default::conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations){
|
||||
return true;
|
||||
}
|
||||
@ -291,7 +296,8 @@ namespace hw {
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key) {
|
||||
std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key,
|
||||
const bool use_view_tags, crypto::view_tag &view_tag) {
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
|
||||
@ -331,6 +337,12 @@ namespace hw {
|
||||
derivation_to_scalar(derivation, output_index, scalar1);
|
||||
amount_keys.push_back(rct::sk2rct(scalar1));
|
||||
}
|
||||
|
||||
if (use_view_tags)
|
||||
{
|
||||
derive_view_tag(derivation, output_index, view_tag);
|
||||
}
|
||||
|
||||
r = derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
|
||||
|
||||
|
@ -101,6 +101,7 @@ namespace hw {
|
||||
bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override;
|
||||
bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override;
|
||||
bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override;
|
||||
bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag);
|
||||
|
||||
|
||||
/* ======================================================================= */
|
||||
@ -126,7 +127,8 @@ namespace hw {
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) override;
|
||||
crypto::public_key &out_eph_public_key,
|
||||
bool use_view_tags, crypto::view_tag &view_tag) override;
|
||||
|
||||
bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
|
||||
bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override;
|
||||
|
@ -1527,7 +1527,8 @@ namespace hw {
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) {
|
||||
crypto::public_key &out_eph_public_key,
|
||||
bool use_view_tags, crypto::view_tag &view_tag) {
|
||||
AUTO_LOCK_CMD();
|
||||
|
||||
#ifdef DEBUG_HWDEVICE
|
||||
@ -1541,6 +1542,8 @@ namespace hw {
|
||||
const boost::optional<cryptonote::account_public_address> change_addr_x = change_addr;
|
||||
const size_t output_index_x = output_index;
|
||||
const bool need_additional_txkeys_x = need_additional_txkeys;
|
||||
const bool use_view_tags_x = use_view_tags;
|
||||
const crypto::view_tag view_tag_x = view_tag;
|
||||
|
||||
std::vector<crypto::secret_key> additional_tx_keys_x;
|
||||
for (const auto &k: additional_tx_keys) {
|
||||
@ -1568,7 +1571,7 @@ namespace hw {
|
||||
log_hexbuffer("generate_output_ephemeral_keys: [[IN]] additional_tx_keys[oi]", additional_tx_keys_x[output_index].data, 32);
|
||||
}
|
||||
this->controle_device->generate_output_ephemeral_keys(tx_version_x, sender_account_keys_x, txkey_pub_x, tx_key_x, dst_entr_x, change_addr_x, output_index_x, need_additional_txkeys_x, additional_tx_keys_x,
|
||||
additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x);
|
||||
additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x, use_view_tags_x, view_tag_x);
|
||||
if(need_additional_txkeys_x) {
|
||||
log_hexbuffer("additional_tx_public_keys_x: [[OUT]] additional_tx_public_keys_x", additional_tx_public_keys_x.back().data, 32);
|
||||
}
|
||||
|
@ -273,7 +273,8 @@ namespace hw {
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) override;
|
||||
crypto::public_key &out_eph_public_key,
|
||||
const bool use_view_tags, crypto::view_tag &view_tag) override;
|
||||
|
||||
bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
|
||||
bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override;
|
||||
|
@ -154,8 +154,7 @@ namespace ki {
|
||||
|
||||
res.emplace_back();
|
||||
auto & cres = res.back();
|
||||
|
||||
cres.set_out_key(key_to_string(boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key));
|
||||
cres.set_out_key(key_to_string(td.get_public_key()));
|
||||
cres.set_tx_pub_key(key_to_string(tx_pub_key));
|
||||
cres.set_internal_output_index(td.m_internal_output_index);
|
||||
cres.set_sub_addr_major(td.m_subaddr_index.major);
|
||||
|
@ -85,6 +85,7 @@ BLOB_SERIALIZER(crypto::secret_key);
|
||||
BLOB_SERIALIZER(crypto::key_derivation);
|
||||
BLOB_SERIALIZER(crypto::key_image);
|
||||
BLOB_SERIALIZER(crypto::signature);
|
||||
BLOB_SERIALIZER(crypto::view_tag);
|
||||
VARIANT_TAG(debug_archive, crypto::hash, "hash");
|
||||
VARIANT_TAG(debug_archive, crypto::hash8, "hash8");
|
||||
VARIANT_TAG(debug_archive, crypto::public_key, "public_key");
|
||||
@ -92,4 +93,5 @@ VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key");
|
||||
VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation");
|
||||
VARIANT_TAG(debug_archive, crypto::key_image, "key_image");
|
||||
VARIANT_TAG(debug_archive, crypto::signature, "signature");
|
||||
VARIANT_TAG(debug_archive, crypto::view_tag, "view_tag");
|
||||
|
||||
|
@ -563,6 +563,27 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout)
|
||||
GET_FROM_JSON_OBJECT(val, txout.key, key);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_tagged_key& txout)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, key, txout.key);
|
||||
INSERT_INTO_JSON_OBJECT(dest, view_tag, txout.view_tag);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_tagged_key& txout)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, txout.key, key);
|
||||
GET_FROM_JSON_OBJECT(val, txout.view_tag, view_tag);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_out& txout)
|
||||
{
|
||||
dest.StartObject();
|
||||
@ -578,6 +599,10 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, to_key, output);
|
||||
}
|
||||
void operator()(cryptonote::txout_to_tagged_key const& output) const
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, to_tagged_key, output);
|
||||
}
|
||||
void operator()(cryptonote::txout_to_script const& output) const
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, to_script, output);
|
||||
@ -616,6 +641,12 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
|
||||
fromJsonValue(elem.value, tmpVal);
|
||||
txout.target = std::move(tmpVal);
|
||||
}
|
||||
else if (elem.name == "to_tagged_key")
|
||||
{
|
||||
cryptonote::txout_to_tagged_key tmpVal;
|
||||
fromJsonValue(elem.value, tmpVal);
|
||||
txout.target = std::move(tmpVal);
|
||||
}
|
||||
else if (elem.name == "to_script")
|
||||
{
|
||||
cryptonote::txout_to_script tmpVal;
|
||||
|
@ -230,6 +230,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash&
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_key& txout);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_tagged_key& txout);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_tagged_key& txout);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_out& txout);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout);
|
||||
|
||||
|
@ -1743,6 +1743,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
|
||||
m_wallet->use_fork_rules(8, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_VIEW_TAGS, 0),
|
||||
m_wallet->get_base_fee(),
|
||||
m_wallet->get_fee_quantization_mask());
|
||||
}
|
||||
|
@ -781,7 +781,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
|
||||
}
|
||||
}
|
||||
|
||||
size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
|
||||
size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
@ -821,6 +821,9 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
|
||||
else
|
||||
size += n_inputs * (64 * (mixin+1) + 32);
|
||||
|
||||
if (use_view_tags)
|
||||
size += n_outputs * sizeof(crypto::view_tag);
|
||||
|
||||
// mixRing - not serialized, can be reconstructed
|
||||
/* size += 2 * 32 * (mixin+1) * n_inputs; */
|
||||
|
||||
@ -837,17 +840,17 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
|
||||
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
|
||||
{
|
||||
if (use_rct)
|
||||
return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
else
|
||||
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
|
||||
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size + (use_view_tags ? (n_outputs * sizeof(crypto::view_tag)) : 0);
|
||||
}
|
||||
|
||||
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
|
||||
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
|
||||
{
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
if (use_rct && (bulletproof || bulletproof_plus) && n_outputs > 2)
|
||||
{
|
||||
const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
|
||||
@ -878,6 +881,11 @@ uint8_t get_clsag_fork()
|
||||
return HF_VERSION_CLSAG;
|
||||
}
|
||||
|
||||
uint8_t get_view_tag_fork()
|
||||
{
|
||||
return HF_VERSION_VIEW_TAGS;
|
||||
}
|
||||
|
||||
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_quantization_mask)
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
@ -1765,13 +1773,14 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
boost::unique_lock<hw::device> hwdev_lock (hwdev);
|
||||
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
|
||||
if (o.target.type() != typeid(txout_to_key))
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(o, output_public_key))
|
||||
{
|
||||
tx_scan_info.error = true;
|
||||
LOG_ERROR("wrong type id in transaction out");
|
||||
return;
|
||||
}
|
||||
tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation, additional_derivations, i, hwdev);
|
||||
tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(o));
|
||||
if(tx_scan_info.received)
|
||||
{
|
||||
tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
|
||||
@ -1856,17 +1865,20 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
}
|
||||
}
|
||||
|
||||
crypto::public_key output_public_key;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key");
|
||||
|
||||
if (m_multisig)
|
||||
{
|
||||
tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
|
||||
tx_scan_info.in_ephemeral.pub = output_public_key;
|
||||
tx_scan_info.in_ephemeral.sec = crypto::null_skey;
|
||||
tx_scan_info.ki = rct::rct2ki(rct::zero());
|
||||
}
|
||||
else
|
||||
{
|
||||
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device());
|
||||
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device());
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
|
||||
THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
|
||||
THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
}
|
||||
|
||||
@ -1993,8 +2005,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
|
||||
int num_vouts_received = 0;
|
||||
tx_pub_key = pub_key_field.pub_key;
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
const cryptonote::account_keys& keys = m_account.get_keys();
|
||||
crypto::key_derivation derivation;
|
||||
|
||||
@ -2064,10 +2074,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
// the first one was already checked
|
||||
for (size_t i = 1; i < tx.vout.size(); ++i)
|
||||
{
|
||||
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
|
||||
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
|
||||
check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]);
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
|
||||
// then scan all outputs from 0
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
boost::unique_lock<hw::device> hwdev_lock (hwdev);
|
||||
@ -2087,32 +2095,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr)
|
||||
{
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
|
||||
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
|
||||
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
boost::unique_lock<hw::device> hwdev_lock (hwdev);
|
||||
hwdev.set_mode(hw::device::NONE);
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
|
||||
if (tx_scan_info[i].received)
|
||||
{
|
||||
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
|
||||
scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool);
|
||||
if (!tx_scan_info[i].error)
|
||||
{
|
||||
tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
@ -2793,25 +2775,34 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
|
||||
for (size_t k = 0; k < n_vouts; ++k)
|
||||
{
|
||||
const auto &o = tx.vout[k];
|
||||
if (o.target.type() == typeid(cryptonote::txout_to_key))
|
||||
crypto::public_key output_public_key;
|
||||
if (get_output_public_key(o, output_public_key))
|
||||
{
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
additional_derivations.reserve(tx_cache_data[txidx].additional.size());
|
||||
for (const auto &iod: tx_cache_data[txidx].additional)
|
||||
additional_derivations.push_back(iod.derivation);
|
||||
const auto &key = boost::get<txout_to_key>(o.target).key;
|
||||
for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
|
||||
error::wallet_internal_error, "Unexpected received array size");
|
||||
tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
|
||||
tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, output_public_key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev, get_output_view_tag(o));
|
||||
additional_derivations.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
struct geniod_params
|
||||
{
|
||||
const cryptonote::transaction &tx;
|
||||
size_t n_outs;
|
||||
size_t txidx;
|
||||
};
|
||||
std::vector<geniod_params> geniods;
|
||||
geniods.reserve(num_txes);
|
||||
|
||||
txidx = 0;
|
||||
uint8_t hf_version_view_tags = get_view_tag_fork();
|
||||
for (size_t i = 0; i < blocks.size(); ++i)
|
||||
{
|
||||
if (should_skip_block(parsed_blocks[i].block, start_height + i))
|
||||
@ -2825,18 +2816,51 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
|
||||
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
|
||||
const cryptonote::transaction& tx = parsed_blocks[i].block.miner_tx;
|
||||
const size_t n_vouts = (m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2) ? 1 : tx.vout.size();
|
||||
tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true);
|
||||
if (parsed_blocks[i].block.major_version >= hf_version_view_tags)
|
||||
geniods.push_back(geniod_params{ tx, n_vouts, txidx });
|
||||
else
|
||||
tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true);
|
||||
}
|
||||
++txidx;
|
||||
for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
|
||||
tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
|
||||
if (parsed_blocks[i].block.major_version >= hf_version_view_tags)
|
||||
geniods.push_back(geniod_params{ parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx });
|
||||
else
|
||||
tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
|
||||
++txidx;
|
||||
}
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
|
||||
|
||||
// View tags significantly speed up the geniod function that determines if an output belongs to the account.
|
||||
// Because the speedup is so large, the overhead from submitting individual geniods to the thread pool eats into
|
||||
// the benefit of executing in parallel. So to maximize the benefit from threads when view tags are enabled,
|
||||
// the wallet starts submitting geniod function calls to the thread pool in batches of size GENIOD_BATCH_SIZE.
|
||||
if (geniods.size())
|
||||
{
|
||||
size_t GENIOD_BATCH_SIZE = 100;
|
||||
size_t num_batch_txes = 0;
|
||||
size_t batch_start = 0;
|
||||
while (batch_start < geniods.size())
|
||||
{
|
||||
size_t batch_end = std::min(batch_start + GENIOD_BATCH_SIZE, geniods.size());
|
||||
THROW_WALLET_EXCEPTION_IF(batch_end < batch_start, error::wallet_internal_error, "Thread batch end overflow");
|
||||
tpool.submit(&waiter, [&geniods, &geniod, batch_start, batch_end]() {
|
||||
for (size_t i = batch_start; i < batch_end; ++i)
|
||||
{
|
||||
const geniod_params &gp = geniods[i];
|
||||
geniod(gp.tx, gp.n_outs, gp.txidx);
|
||||
}
|
||||
}, true);
|
||||
num_batch_txes += batch_end - batch_start;
|
||||
batch_start = batch_end;
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(num_batch_txes != geniods.size(), error::wallet_internal_error, "txes batched for thread pool did not reach expected value");
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
|
||||
|
||||
hwdev.set_mode(hw::device::NONE);
|
||||
|
||||
size_t tx_cache_data_offset = 0;
|
||||
@ -6613,7 +6637,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
rct::multisig_out msout;
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL, sd.use_view_tags);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
||||
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
|
||||
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
|
||||
@ -6698,16 +6722,17 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(tx.vout[i], output_public_key))
|
||||
continue;
|
||||
const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
|
||||
|
||||
// if this output is back to this wallet, we can calculate its key image already
|
||||
if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
|
||||
if (!is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(tx.vout[i])))
|
||||
continue;
|
||||
crypto::key_image ki;
|
||||
cryptonote::keypair in_ephemeral;
|
||||
if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
|
||||
signed_txes.tx_key_images[out.key] = ki;
|
||||
if (generate_key_image_helper(keys, m_subaddresses, output_public_key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
|
||||
signed_txes.tx_key_images[output_public_key] = ki;
|
||||
else
|
||||
MERROR("Failed to calculate key image");
|
||||
}
|
||||
@ -7132,7 +7157,8 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
|
||||
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
|
||||
auto sources = sd.sources;
|
||||
rct::RCTConfig rct_config = sd.rct_config;
|
||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false);
|
||||
bool shuffle_outs = false;
|
||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, shuffle_outs, sd.use_view_tags);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
|
||||
@ -7232,16 +7258,16 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
|
||||
return sign_multisig_tx_to_file(exported_txs, filename, txids);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_quantization_mask) const
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
{
|
||||
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
return calculate_fee(base_fee, estimated_tx_size);
|
||||
}
|
||||
}
|
||||
@ -8461,7 +8487,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||
{
|
||||
size_t i = base + n;
|
||||
if (req.outputs[i].index == td.m_global_output_index)
|
||||
if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
|
||||
if (daemon_resp.outs[i].key == td.get_public_key())
|
||||
if (daemon_resp.outs[i].mask == mask)
|
||||
if (daemon_resp.outs[i].unlocked)
|
||||
real_out_found = true;
|
||||
@ -8470,7 +8496,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||
"Daemon response did not include the requested real output");
|
||||
|
||||
// pick real out first (it will be sorted when done)
|
||||
outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
|
||||
outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask));
|
||||
|
||||
// then pick outs from an existing ring, if any
|
||||
if (td.m_key_image_known && !td.m_key_image_partial)
|
||||
@ -8561,7 +8587,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||
template<typename T>
|
||||
void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx,
|
||||
bool use_view_tags)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
// throw if attempting a transaction with no destinations
|
||||
@ -8634,7 +8661,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||
|
||||
tx_output_entry real_oe;
|
||||
real_oe.first = td.m_global_output_index;
|
||||
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
|
||||
real_oe.second.dest = rct::pk2rct(td.get_public_key());
|
||||
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
|
||||
*it_to_replace = real_oe;
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
|
||||
@ -8672,7 +8699,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
rct::multisig_out msout;
|
||||
LOG_PRINT_L2("constructing tx");
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
|
||||
@ -8710,6 +8737,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||
ptx.construction_data.unlock_time = unlock_time;
|
||||
ptx.construction_data.use_rct = false;
|
||||
ptx.construction_data.rct_config = { rct::RangeProofBorromean, 0 };
|
||||
ptx.construction_data.use_view_tags = use_view_tags;
|
||||
ptx.construction_data.dests = dsts;
|
||||
// record which subaddress indices are being used as inputs
|
||||
ptx.construction_data.subaddr_account = subaddr_account;
|
||||
@ -8721,7 +8749,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||
|
||||
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config)
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
// throw if attempting a transaction with no destinations
|
||||
@ -8906,7 +8934,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
rct::multisig_out msout;
|
||||
LOG_PRINT_L2("constructing tx");
|
||||
auto sources_copy = sources;
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, m_multisig ? &msout : NULL);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, m_multisig ? &msout : NULL, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
|
||||
@ -8951,7 +8979,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
LOG_PRINT_L2("Creating supplementary multisig transaction");
|
||||
cryptonote::transaction ms_tx;
|
||||
auto sources_copy_copy = sources_copy;
|
||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, rct_config, &msout, false);
|
||||
bool shuffle_outs = false;
|
||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, rct_config, &msout, shuffle_outs, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
|
||||
@ -8998,6 +9027,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
rct::RangeProofPaddedBulletproof,
|
||||
use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 : 3
|
||||
};
|
||||
ptx.construction_data.use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
|
||||
ptx.construction_data.dests = dsts;
|
||||
// record which subaddress indices are being used as inputs
|
||||
ptx.construction_data.subaddr_account = subaddr_account;
|
||||
@ -9697,6 +9727,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
rct::RangeProofPaddedBulletproof,
|
||||
bulletproof_plus ? 4 : 3
|
||||
};
|
||||
const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
|
||||
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
|
||||
@ -9730,7 +9761,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
// early out if we know we can't make it anyway
|
||||
// we could also check for being within FEE_PER_KB, but if the fee calculation
|
||||
// ever changes, this might be missed, so let this go through
|
||||
const uint64_t min_fee = (base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus));
|
||||
const uint64_t min_fee = (base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags));
|
||||
uint64_t balance_subtotal = 0;
|
||||
uint64_t unlocked_balance_subtotal = 0;
|
||||
for (uint32_t index_minor : subaddr_indices)
|
||||
@ -9748,8 +9779,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
|
||||
|
||||
// determine threshold for fractional amount
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
@ -9846,7 +9877,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
{
|
||||
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
|
||||
// will get us a known fee.
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
|
||||
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
|
||||
if (!preferred_inputs.empty())
|
||||
{
|
||||
@ -9959,7 +9990,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
|
||||
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
|
||||
{
|
||||
// we can fully pay that destination
|
||||
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
|
||||
@ -9977,7 +10008,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
}
|
||||
|
||||
if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() &&
|
||||
estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
|
||||
estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
|
||||
// we can partially fill that destination
|
||||
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
|
||||
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
|
||||
@ -10015,7 +10046,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
|
||||
THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
|
||||
}
|
||||
@ -10026,7 +10057,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
pending_tx test_ptx;
|
||||
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
|
||||
|
||||
auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
|
||||
{
|
||||
@ -10075,10 +10106,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
tx.selected_transfers.size() << " inputs");
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config);
|
||||
test_tx, test_ptx, rct_config, use_view_tags);
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
|
||||
@ -10100,10 +10131,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
while (needed_fee > test_ptx.fee) {
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config);
|
||||
test_tx, test_ptx, rct_config, use_view_tags);
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
|
||||
@ -10173,7 +10204,8 @@ skip_tx:
|
||||
extra, /* const std::vector<uint8_t>& extra, */
|
||||
test_tx, /* OUT cryptonote::transaction& tx, */
|
||||
test_ptx, /* OUT cryptonote::transaction& tx, */
|
||||
rct_config);
|
||||
rct_config,
|
||||
use_view_tags); /* const bool use_view_tags */
|
||||
} else {
|
||||
transfer_selected(tx.dsts,
|
||||
tx.selected_transfers,
|
||||
@ -10185,7 +10217,8 @@ skip_tx:
|
||||
detail::digit_split_strategy,
|
||||
tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD),
|
||||
test_tx,
|
||||
test_ptx);
|
||||
test_ptx,
|
||||
use_view_tags);
|
||||
}
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
tx.tx = test_tx;
|
||||
@ -10288,9 +10321,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
@ -10402,6 +10436,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
rct::RangeProofPaddedBulletproof,
|
||||
bulletproof_plus ? 4 : 3
|
||||
};
|
||||
const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
|
||||
|
||||
@ -10428,7 +10463,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
uint64_t fee_dust_threshold;
|
||||
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
|
||||
{
|
||||
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
|
||||
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
@ -10459,7 +10494,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
// here, check if we need to sent tx and start a new one
|
||||
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
|
||||
<< upper_transaction_weight_limit);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag, bulletproof_plus);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
|
||||
|
||||
if (try_tx) {
|
||||
@ -10467,7 +10502,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
pending_tx test_ptx;
|
||||
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
|
||||
|
||||
// add N - 1 outputs for correct initial fee estimation
|
||||
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
|
||||
@ -10477,10 +10512,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
tx.selected_transfers.size() << " outputs");
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config);
|
||||
test_tx, test_ptx, rct_config, use_view_tags);
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
|
||||
@ -10514,10 +10549,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
}
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config);
|
||||
test_tx, test_ptx, rct_config, use_view_tags);
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
|
||||
@ -10553,10 +10588,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
pending_tx test_ptx;
|
||||
if (use_rct) {
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config);
|
||||
test_tx, test_ptx, rct_config, use_view_tags);
|
||||
} else {
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
}
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
tx.tx = test_tx;
|
||||
@ -11095,13 +11130,12 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
|
||||
|
||||
// derive the real output keypair
|
||||
const transfer_details& in_td = m_transfers[found->second];
|
||||
const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
|
||||
THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
|
||||
crypto::public_key in_tx_out_pkey = in_td.get_public_key();
|
||||
const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
|
||||
const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
|
||||
keypair in_ephemeral;
|
||||
crypto::key_image in_img;
|
||||
THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()),
|
||||
THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()),
|
||||
error::wallet_internal_error, "failed to generate key image");
|
||||
THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
|
||||
|
||||
@ -11300,24 +11334,12 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
|
||||
|
||||
for (size_t n = 0; n < tx.vout.size(); ++n)
|
||||
{
|
||||
const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
|
||||
if (!out_key)
|
||||
crypto::public_key output_public_key;
|
||||
if (!get_output_public_key(tx.vout[n], output_public_key))
|
||||
continue;
|
||||
|
||||
crypto::public_key derived_out_key;
|
||||
bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
|
||||
bool found = out_key->key == derived_out_key;
|
||||
crypto::key_derivation found_derivation = derivation;
|
||||
if (!found && !additional_derivations.empty())
|
||||
{
|
||||
r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
|
||||
found = out_key->key == derived_out_key;
|
||||
found_derivation = additional_derivations[n];
|
||||
}
|
||||
|
||||
if (found)
|
||||
crypto::key_derivation found_derivation;
|
||||
if (is_out_to_acc(address, output_public_key, derivation, additional_derivations, n, get_output_view_tag(tx.vout[n]), found_derivation))
|
||||
{
|
||||
uint64_t amount;
|
||||
if (tx.version == 1 || tx.rct_signatures.type == rct::RCTTypeNull)
|
||||
@ -11411,6 +11433,42 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
|
||||
}
|
||||
}
|
||||
|
||||
bool wallet2::is_out_to_acc(const cryptonote::account_public_address &address, const crypto::public_key& out_key, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const size_t output_index, const boost::optional<crypto::view_tag> &view_tag_opt, crypto::key_derivation &found_derivation) const
|
||||
{
|
||||
crypto::public_key derived_out_key;
|
||||
bool found = false;
|
||||
bool r;
|
||||
// first run quick check if output has matching view tag, otherwise output should not belong to account
|
||||
if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
|
||||
{
|
||||
// if view tag match, run slower check deriving output pub key and comparing to expected
|
||||
r = crypto::derive_public_key(derivation, output_index, address.m_spend_public_key, derived_out_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
|
||||
if (out_key == derived_out_key)
|
||||
{
|
||||
found = true;
|
||||
found_derivation = derivation;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && !additional_derivations.empty())
|
||||
{
|
||||
const crypto::key_derivation &additional_derivation = additional_derivations[output_index];
|
||||
if (out_can_be_to_acc(view_tag_opt, additional_derivation, output_index))
|
||||
{
|
||||
r = crypto::derive_public_key(additional_derivation, output_index, address.m_spend_public_key, derived_out_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
|
||||
if (out_key == derived_out_key)
|
||||
{
|
||||
found = true;
|
||||
found_derivation = additional_derivation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
|
||||
{
|
||||
// fetch tx pubkey from the daemon
|
||||
@ -11947,8 +12005,8 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
|
||||
|
||||
const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
|
||||
THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
|
||||
crypto::public_key output_public_key;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[proof.index_in_tx], output_public_key), error::wallet_internal_error, "Output key wasn't found");
|
||||
|
||||
// get tx pub key
|
||||
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
|
||||
@ -11963,7 +12021,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
|
||||
return false;
|
||||
|
||||
// check signature for key image
|
||||
const std::vector<const crypto::public_key*> pubs = { &out_key->key };
|
||||
const std::vector<const crypto::public_key*> pubs = { &output_public_key };
|
||||
ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
|
||||
if (!ok)
|
||||
return false;
|
||||
@ -11972,7 +12030,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
|
||||
crypto::key_derivation derivation;
|
||||
THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
|
||||
crypto::public_key subaddr_spendkey;
|
||||
crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
|
||||
crypto::derive_subaddress_public_key(output_public_key, derivation, proof.index_in_tx, subaddr_spendkey);
|
||||
THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
|
||||
"The address doesn't seem to have received the fund");
|
||||
|
||||
@ -12426,11 +12484,7 @@ std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>
|
||||
const transfer_details &td = m_transfers[n];
|
||||
|
||||
// get ephemeral public key
|
||||
const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
|
||||
THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error,
|
||||
"Output is not txout_to_key");
|
||||
const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
|
||||
const crypto::public_key pkey = o.key;
|
||||
const crypto::public_key pkey = td.get_public_key();
|
||||
|
||||
// get tx pub key
|
||||
std::vector<tx_extra_field> tx_extra_fields;
|
||||
@ -12547,11 +12601,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
|
||||
const crypto::signature &signature = signed_key_images[n].second;
|
||||
|
||||
// get ephemeral public key
|
||||
const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
|
||||
THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error,
|
||||
"Non txout_to_key output found");
|
||||
const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
|
||||
const crypto::public_key pkey = o.key;
|
||||
const crypto::public_key pkey = td.get_public_key();
|
||||
|
||||
if (!td.m_key_image_known || !(key_image == td.m_key_image))
|
||||
{
|
||||
@ -13007,9 +13057,7 @@ process:
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_internal_output_index >= td.m_tx.vout.size(),
|
||||
error::wallet_internal_error, "Internal index is out of range");
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
|
||||
error::wallet_internal_error, "Unsupported output type");
|
||||
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
|
||||
crypto::public_key out_key = td.get_public_key();
|
||||
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
|
||||
if (should_expand(td.m_subaddr_index))
|
||||
@ -14235,8 +14283,9 @@ std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, i
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
|
||||
return std::make_pair(size, weight);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -347,7 +347,12 @@ private:
|
||||
|
||||
bool is_rct() const { return m_rct; }
|
||||
uint64_t amount() const { return m_amount; }
|
||||
const crypto::public_key &get_public_key() const { return boost::get<const cryptonote::txout_to_key>(m_tx.vout[m_internal_output_index].target).key; }
|
||||
const crypto::public_key get_public_key() const {
|
||||
crypto::public_key output_public_key;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(m_tx.vout[m_internal_output_index], output_public_key),
|
||||
error::wallet_internal_error, "Unable to get output public key from output");
|
||||
return output_public_key;
|
||||
};
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(m_block_height)
|
||||
@ -529,10 +534,21 @@ private:
|
||||
uint64_t unlock_time;
|
||||
bool use_rct;
|
||||
rct::RCTConfig rct_config;
|
||||
bool use_view_tags;
|
||||
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
|
||||
uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer
|
||||
std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer
|
||||
|
||||
enum construction_flags_ : uint8_t
|
||||
{
|
||||
_use_rct = 1 << 0, // 00000001
|
||||
_use_view_tags = 1 << 1 // 00000010
|
||||
// next flag = 1 << 2 // 00000100
|
||||
// ...
|
||||
// final flag = 1 << 7 // 10000000
|
||||
};
|
||||
uint8_t construction_flags;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(sources)
|
||||
FIELD(change_dts)
|
||||
@ -540,7 +556,26 @@ private:
|
||||
FIELD(selected_transfers)
|
||||
FIELD(extra)
|
||||
FIELD(unlock_time)
|
||||
FIELD(use_rct)
|
||||
|
||||
// converted `use_rct` field into construction_flags when view tags
|
||||
// were introduced to maintain backwards compatibility
|
||||
if (!typename Archive<W>::is_saving())
|
||||
{
|
||||
FIELD_N("use_rct", construction_flags)
|
||||
use_rct = (construction_flags & _use_rct) > 0;
|
||||
use_view_tags = (construction_flags & _use_view_tags) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
construction_flags = 0;
|
||||
if (use_rct)
|
||||
construction_flags ^= _use_rct;
|
||||
if (use_view_tags)
|
||||
construction_flags ^= _use_view_tags;
|
||||
|
||||
FIELD_N("use_rct", construction_flags)
|
||||
}
|
||||
|
||||
FIELD(rct_config)
|
||||
FIELD(dests)
|
||||
FIELD(subaddr_account)
|
||||
@ -967,10 +1002,10 @@ private:
|
||||
template<typename T>
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags);
|
||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config);
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags);
|
||||
|
||||
void commit_tx(pending_tx& ptx_vector);
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
@ -1090,9 +1125,7 @@ private:
|
||||
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||
{
|
||||
const transfer_details &td = m_transfers[i];
|
||||
const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
|
||||
const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
|
||||
m_pub_keys.emplace(o.key, i);
|
||||
m_pub_keys.emplace(td.get_public_key(), i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1271,6 +1304,7 @@ private:
|
||||
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
|
||||
void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
|
||||
void check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const;
|
||||
bool is_out_to_acc(const cryptonote::account_public_address &address, const crypto::public_key& out_key, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const size_t output_index, const boost::optional<crypto::view_tag> &view_tag_opt, crypto::key_derivation &found_derivation) const;
|
||||
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
|
||||
std::string get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const;
|
||||
bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations);
|
||||
@ -1427,7 +1461,7 @@ private:
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
|
||||
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_quantization_mask) const;
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const;
|
||||
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
||||
uint64_t get_base_fee(uint32_t priority);
|
||||
uint64_t get_base_fee();
|
||||
|
@ -671,3 +671,127 @@ bool gen_block_low_coinbase::generate(std::vector<test_event_entry>& events) con
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
bool use_view_tags = false;
|
||||
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
|
||||
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag");
|
||||
|
||||
crypto::public_key output_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key);
|
||||
|
||||
// explicitly call the setter to ensure it does not set a view tag on the miner tx output
|
||||
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
||||
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should still not have a view tag");
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
bool use_view_tags = false;
|
||||
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
keypair txkey;
|
||||
MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS+1, &txkey);
|
||||
|
||||
crypto::public_key output_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key);
|
||||
|
||||
// remove the view tag that is currently set on the miner tx output at this point
|
||||
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
||||
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag");
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx,
|
||||
HF_VERSION_VIEW_TAGS+1, HF_VERSION_VIEW_TAGS+1, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_has_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
bool use_view_tags = true;
|
||||
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
keypair txkey;
|
||||
MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey);
|
||||
|
||||
// derive the view tag for the miner tx output
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key output_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key);
|
||||
crypto::derive_view_tag(derivation, 0, view_tag);
|
||||
|
||||
// set the view tag on the miner tx output
|
||||
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
||||
boost::optional<crypto::view_tag> actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
||||
CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag");
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_has_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
bool use_view_tags = true;
|
||||
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
keypair txkey;
|
||||
MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS, &txkey);
|
||||
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should have a view tag");
|
||||
|
||||
// derive the view tag for the miner tx output
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key output_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key);
|
||||
crypto::derive_view_tag(derivation, 0, view_tag);
|
||||
|
||||
boost::optional<crypto::view_tag> actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
||||
CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag");
|
||||
|
||||
// set the view tag on the miner tx output
|
||||
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
||||
boost::optional<crypto::view_tag> actual_vt_after_setting = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
||||
CHECK_AND_ASSERT_MES(actual_vt_after_setting && *actual_vt_after_setting == view_tag, false, "unexpected output view tag after setting");
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx,
|
||||
HF_VERSION_VIEW_TAGS, HF_VERSION_VIEW_TAGS, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -230,3 +230,37 @@ struct get_test_options<gen_block_low_coinbase> {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags : public gen_block_accepted_base<2>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<>
|
||||
struct get_test_options<gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_VIEW_TAGS+1, 1), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_has_view_tag_before_hf_view_tags : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_has_view_tag_from_hf_view_tags : public gen_block_accepted_base<2>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<>
|
||||
struct get_test_options<gen_block_miner_tx_out_has_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_VIEW_TAGS, 1), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
@ -462,7 +462,9 @@ bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector<
|
||||
size_t tx_global_idx = outs[out.amount].size() - 1;
|
||||
outs[out.amount][tx_global_idx].idx = tx_global_idx;
|
||||
// Is out to me?
|
||||
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) {
|
||||
crypto::public_key output_public_key;
|
||||
cryptonote::get_output_public_key(out, output_public_key);
|
||||
if (is_out_to_acc(from.get_keys(), output_public_key, get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) {
|
||||
outs_mine[out.amount].push_back(tx_global_idx);
|
||||
}
|
||||
}
|
||||
@ -972,7 +974,7 @@ std::vector<cryptonote::tx_destination_entry> build_dsts(std::initializer_list<d
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const account_public_address& miner_address, transaction& tx, uint64_t fee,
|
||||
keypair* p_txkey/* = 0*/)
|
||||
uint8_t hf_version/* = 1*/, keypair* p_txkey/* = 0*/)
|
||||
{
|
||||
keypair txkey;
|
||||
txkey = keypair::generate(hw::get_device("default"));
|
||||
@ -987,7 +989,7 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
|
||||
|
||||
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
||||
uint64_t block_reward;
|
||||
if (!get_block_reward(0, 0, already_generated_coins, block_reward, 1))
|
||||
if (!get_block_reward(0, 0, already_generated_coins, block_reward, hf_version))
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
@ -999,12 +1001,20 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
|
||||
crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
|
||||
bool use_view_tags = hf_version >= HF_VERSION_VIEW_TAGS;
|
||||
crypto::view_tag view_tag;
|
||||
if (use_view_tags)
|
||||
crypto::derive_view_tag(derivation, 0, view_tag);
|
||||
|
||||
tx_out out;
|
||||
out.amount = block_reward;
|
||||
out.target = txout_to_key(out_eph_public_key);
|
||||
cryptonote::set_tx_out(block_reward, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
|
||||
tx.vout.push_back(out);
|
||||
|
||||
tx.version = 1;
|
||||
if (hf_version >= HF_VERSION_DYNAMIC_FEE)
|
||||
tx.version = 2;
|
||||
else
|
||||
tx.version = 1;
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
return true;
|
||||
|
@ -423,7 +423,8 @@ uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources);
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx,
|
||||
uint64_t fee, cryptonote::keypair* p_txkey = nullptr);
|
||||
uint64_t fee, uint8_t hf_version = 1,
|
||||
cryptonote::keypair* p_txkey = nullptr);
|
||||
|
||||
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx,
|
||||
const cryptonote::block& blk_head, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount,
|
||||
@ -967,12 +968,14 @@ inline bool do_replay_file(const std::string& filename)
|
||||
std::list<cryptonote::transaction> SET_NAME; \
|
||||
MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD);
|
||||
|
||||
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \
|
||||
#define MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(TX, BLK, HF_VERSION, KEY) \
|
||||
transaction TX; \
|
||||
if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \
|
||||
miner_account.get_keys().m_account_address, TX, 0, KEY)) \
|
||||
miner_account.get_keys().m_account_address, TX, 0, HF_VERSION, KEY)) \
|
||||
return false;
|
||||
|
||||
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(TX, BLK, 1, KEY)
|
||||
|
||||
#define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0)
|
||||
|
||||
#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT) VEC_EVENTS.push_back(event_visitor_settings(SETT));
|
||||
|
@ -133,6 +133,10 @@ int main(int argc, char* argv[])
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_is_big);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_no_out);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_out_to_alice);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_has_view_tag_before_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_has_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_block_has_invalid_tx);
|
||||
GENERATE_AND_PLAY(gen_block_is_too_big);
|
||||
GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10
|
||||
@ -219,6 +223,15 @@ int main(int argc, char* argv[])
|
||||
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags);
|
||||
// TODO: base test needs to be restructured to handle pre rct outputs after HF v12
|
||||
// GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags);
|
||||
// GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_rct_has_view_tag_before_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_rct_has_view_tag_from_hf_view_tags);
|
||||
GENERATE_AND_PLAY(gen_rct_tx_uses_output_too_early);
|
||||
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2);
|
||||
|
@ -41,7 +41,7 @@ using namespace cryptonote;
|
||||
// Tests
|
||||
|
||||
bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry>& events,
|
||||
const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
|
||||
const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool use_view_tags, bool valid,
|
||||
const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
|
||||
const std::function<void(transaction &tx)> &post_tx) const
|
||||
{
|
||||
@ -98,7 +98,9 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
|
||||
const size_t index_in_tx = 5;
|
||||
src.amount = 30000000000000;
|
||||
for (int m = 0; m < 4; ++m) {
|
||||
src.push_output(m, boost::get<txout_to_key>(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount);
|
||||
crypto::public_key output_public_key;
|
||||
cryptonote::get_output_public_key(blocks[m].miner_tx.vout[index_in_tx], output_public_key);
|
||||
src.push_output(m, output_public_key, src.amount);
|
||||
}
|
||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx);
|
||||
src.real_output = n;
|
||||
@ -139,10 +141,13 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
|
||||
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
|
||||
}
|
||||
|
||||
uint64_t fee = 0;
|
||||
get_tx_fee(rct_txes[n], fee);
|
||||
|
||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs | test_generator::bf_tx_fees,
|
||||
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||
crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 4),
|
||||
crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 4, fee),
|
||||
false, "Failed to generate block");
|
||||
events.push_back(blk_txes[n]);
|
||||
blk_last = blk_txes[n];
|
||||
@ -224,7 +229,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true, rct_config);
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true, rct_config, NULL, use_view_tags);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||
|
||||
if (post_tx)
|
||||
@ -244,7 +249,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||
const std::function<void(transaction &tx)> &post_tx) const
|
||||
{
|
||||
const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx);
|
||||
bool use_view_tags = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, use_view_tags, valid, pre_tx, post_tx);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_valid_from_pre_rct::generate(std::vector<test_event_entry>& events) const
|
||||
@ -517,11 +523,99 @@ bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& event
|
||||
NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed;
|
||||
}
|
||||
|
||||
bool gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 2;
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
bool use_view_tags = false;
|
||||
bool valid = true;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, {}, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 10;
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 };
|
||||
bool use_view_tags = false;
|
||||
bool valid = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 2;
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
bool use_view_tags = true;
|
||||
bool valid = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, {}, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 10;
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 };
|
||||
bool use_view_tags = true;
|
||||
bool valid = true;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 2;
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
|
||||
bool use_view_tags = false;
|
||||
bool valid = true;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 10;
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 };
|
||||
bool use_view_tags = false;
|
||||
bool valid = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS+1, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_rct_has_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 2;
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
|
||||
bool use_view_tags = true;
|
||||
bool valid = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_rct_has_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 10;
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 };
|
||||
bool use_view_tags = true;
|
||||
bool valid = true;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_rct_tx_uses_output_too_early::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const int mixin = 10;
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 2 };
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL);
|
||||
bool use_view_tags = false;
|
||||
bool valid = false;
|
||||
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, use_view_tags, valid, NULL, NULL);
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base
|
||||
}
|
||||
|
||||
bool generate_with_full(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
|
||||
uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
|
||||
uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool use_view_tags, bool valid,
|
||||
const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
|
||||
const std::function<void(cryptonote::transaction &tx)> &post_tx) const;
|
||||
bool generate_with(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
|
||||
@ -266,6 +266,74 @@ struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_rct_altered_extra>: public get_test_options<gen_rct_tx_validation_base> {};
|
||||
|
||||
struct gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags>: public get_test_options<gen_rct_tx_validation_base> {};
|
||||
|
||||
struct gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags>: public get_test_options<gen_rct_tx_validation_base> {};
|
||||
|
||||
struct gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags>: public get_test_options<gen_rct_tx_validation_base> {};
|
||||
|
||||
struct gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS+1, 69), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_rct_tx_rct_has_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_rct_has_view_tag_before_hf_view_tags>: public get_test_options<gen_rct_tx_validation_base> {};
|
||||
|
||||
struct gen_rct_tx_rct_has_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_rct_tx_rct_has_view_tag_from_hf_view_tags> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks, 0
|
||||
};
|
||||
};
|
||||
|
||||
struct gen_rct_tx_uses_output_too_early : public gen_rct_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
@ -260,7 +260,6 @@ int main(int argc, char *argv[]) {
|
||||
goto error;
|
||||
}
|
||||
} else if (cmd == "check_ge_p3_identity") {
|
||||
cerr << "Testing: " << cmd << endl;
|
||||
public_key point;
|
||||
bool expected_bad, expected_good, result_badfunc, result_goodfunc;
|
||||
get(input, point, expected_bad, expected_good);
|
||||
@ -269,6 +268,15 @@ int main(int argc, char *argv[]) {
|
||||
if (expected_bad != result_badfunc || expected_good != result_goodfunc) {
|
||||
goto error;
|
||||
}
|
||||
} else if (cmd == "derive_view_tag") {
|
||||
key_derivation derivation;
|
||||
size_t output_index;
|
||||
view_tag expected, actual;
|
||||
get(input, derivation, output_index, expected);
|
||||
derive_view_tag(derivation, output_index, actual);
|
||||
if (expected != actual) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
throw ios_base::failure("Unknown function: " + cmd);
|
||||
}
|
||||
|
@ -5473,3 +5473,59 @@ check_ge_p3_identity 046e1450f147f3ade34d149973913cc75d4e7b9669eb1ed61da0f1d4a0b
|
||||
check_ge_p3_identity ca8a2f621cfc7aa3efcd7ddf55dce5352e757b38aca0869b050c0a27824e5c5e true true
|
||||
check_ge_p3_identity 64a247eef6087d86e1e9fa048a3c181fdb1728431f29ba738634bdc38f02a859 true true
|
||||
check_ge_p3_identity cff0c7170a41395b0658ee42b76545c45360736b973ab2f31f6f227b9415df67 true true
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 0 76
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 1 d6
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 2 87
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 3 1b
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 12 d6
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 13 e9
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 14 12
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 15 26
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 0 70
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 1 81
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 2 a0
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 3 ec
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 12 22
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 13 0a
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 14 87
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 15 76
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 0 93
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 1 67
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 2 9d
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 3 2d
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 12 63
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 13 cf
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 14 ef
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 15 10
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 0 90
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 1 5a
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 2 de
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 3 21
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 12 57
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 13 52
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 14 6f
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 15 eb
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 0 c6
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 1 60
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 2 f0
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 3 71
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 12 0e
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 13 42
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 14 b2
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 15 61
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 0 4c
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 1 9b
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 2 64
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 3 ff
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 12 e3
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 13 24
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 14 ea
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 15 3b
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 0 74
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 1 77
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 2 a9
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 3 44
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 12 75
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 13 05
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 14 ca
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00
|
||||
|
@ -43,6 +43,7 @@ set(performance_tests_headers
|
||||
generate_keypair.h
|
||||
signature.h
|
||||
is_out_to_acc.h
|
||||
out_can_be_to_acc.h
|
||||
subaddress_expand.h
|
||||
range_proof.h
|
||||
bulletproof.h
|
||||
|
62
tests/performance_tests/derive_view_tag.h
Normal file
62
tests/performance_tests/derive_view_tag.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2014-2021, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
|
||||
#include "single_tx_test_base.h"
|
||||
|
||||
class test_derive_view_tag : public single_tx_test_base
|
||||
{
|
||||
public:
|
||||
static const size_t loop_count = 10000;
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!single_tx_test_base::init())
|
||||
return false;
|
||||
|
||||
crypto::generate_key_derivation(m_tx_pub_key, m_bob.get_keys().m_view_secret_key, m_key_derivation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test()
|
||||
{
|
||||
crypto::view_tag view_tag;
|
||||
crypto::derive_view_tag(m_key_derivation, 0, view_tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
crypto::key_derivation m_key_derivation;
|
||||
};
|
@ -43,8 +43,9 @@ public:
|
||||
|
||||
bool test()
|
||||
{
|
||||
const cryptonote::txout_to_key& tx_out = boost::get<cryptonote::txout_to_key>(m_tx.vout[0].target);
|
||||
return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, m_additional_tx_pub_keys, 0);
|
||||
crypto::public_key output_public_key;
|
||||
cryptonote::get_output_public_key(m_tx.vout[0], output_public_key);
|
||||
return cryptonote::is_out_to_acc(m_bob.get_keys(), output_public_key, m_tx_pub_key, m_additional_tx_pub_keys, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "cn_slow_hash.h"
|
||||
#include "derive_public_key.h"
|
||||
#include "derive_secret_key.h"
|
||||
#include "derive_view_tag.h"
|
||||
#include "ge_frombytes_vartime.h"
|
||||
#include "ge_tobytes.h"
|
||||
#include "generate_key_derivation.h"
|
||||
@ -50,6 +51,7 @@
|
||||
#include "generate_keypair.h"
|
||||
#include "signature.h"
|
||||
#include "is_out_to_acc.h"
|
||||
#include "out_can_be_to_acc.h"
|
||||
#include "subaddress_expand.h"
|
||||
#include "sc_reduce32.h"
|
||||
#include "sc_check.h"
|
||||
@ -194,6 +196,9 @@ int main(int argc, char** argv)
|
||||
|
||||
TEST_PERFORMANCE0(filter, p, test_is_out_to_acc);
|
||||
TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp);
|
||||
TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, false, true); // no view tag, owned
|
||||
TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, true, false); // use view tag, not owned
|
||||
TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, true, true); // use view tag, owned
|
||||
TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper);
|
||||
TEST_PERFORMANCE0(filter, p, test_generate_key_derivation);
|
||||
TEST_PERFORMANCE0(filter, p, test_generate_key_image);
|
||||
@ -206,6 +211,7 @@ int main(int argc, char** argv)
|
||||
TEST_PERFORMANCE0(filter, p, test_sc_check);
|
||||
TEST_PERFORMANCE1(filter, p, test_signature, false);
|
||||
TEST_PERFORMANCE1(filter, p, test_signature, true);
|
||||
TEST_PERFORMANCE0(filter, p, test_derive_view_tag);
|
||||
|
||||
TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200);
|
||||
|
||||
|
103
tests/performance_tests/out_can_be_to_acc.h
Normal file
103
tests/performance_tests/out_can_be_to_acc.h
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2014-2021, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
|
||||
#include "single_tx_test_base.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
// use_view_tags: whether to enable view tag checking
|
||||
// is_owned: whether the output is owned by us
|
||||
template<bool use_view_tags, bool is_owned>
|
||||
class test_out_can_be_to_acc : public single_tx_test_base
|
||||
{
|
||||
public:
|
||||
static const size_t loop_count = 1000;
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!single_tx_test_base::init())
|
||||
return false;
|
||||
|
||||
crypto::key_derivation key_derivation;
|
||||
crypto::view_tag vt;
|
||||
|
||||
m_output_index = 0;
|
||||
m_view_secret_key = m_bob.get_keys().m_view_secret_key;
|
||||
m_spend_public_key = m_bob.get_keys().m_account_address.m_spend_public_key;
|
||||
|
||||
cryptonote::get_output_public_key(m_tx.vout[m_output_index], m_output_public_key);
|
||||
|
||||
if (use_view_tags)
|
||||
{
|
||||
crypto::generate_key_derivation(m_tx_pub_key, m_view_secret_key, key_derivation);
|
||||
crypto::derive_view_tag(key_derivation, m_output_index, vt);
|
||||
m_view_tag_opt = vt;
|
||||
}
|
||||
else
|
||||
m_view_tag_opt = boost::optional<crypto::view_tag>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test()
|
||||
{
|
||||
// include key derivation to demonstrate performance improvement when using view tags
|
||||
crypto::key_derivation key_derivation;
|
||||
crypto::generate_key_derivation(m_tx_pub_key, m_view_secret_key, key_derivation);
|
||||
|
||||
// if using view tags, this ensures we computed the view tag properly
|
||||
if (!cryptonote::out_can_be_to_acc(m_view_tag_opt, key_derivation, m_output_index))
|
||||
return false;
|
||||
|
||||
// if user owns output, this tests the output public key matches the derived
|
||||
if (is_owned)
|
||||
{
|
||||
crypto::public_key output_public_key;
|
||||
crypto::derive_public_key(key_derivation, m_output_index, m_spend_public_key, output_public_key);
|
||||
|
||||
if (m_output_public_key != output_public_key)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_output_index;
|
||||
crypto::secret_key m_view_secret_key;
|
||||
crypto::public_key m_spend_public_key;
|
||||
crypto::public_key m_output_public_key;
|
||||
boost::optional<crypto::view_tag> m_view_tag_opt;
|
||||
};
|
Loading…
Reference in New Issue
Block a user