Skip to content

Commit

Permalink
Merge pull request #7 from j-berman/hf-15
Browse files Browse the repository at this point in the history
Support for monero hard fork v15 (view tags)
  • Loading branch information
Snipa22 authored Aug 10, 2022
2 parents 1c25fbf + 07fb0a6 commit 58fbe41
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 26 deletions.
24 changes: 24 additions & 0 deletions src/crypto/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,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));
}
}
17 changes: 16 additions & 1 deletion src/crypto/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ namespace crypto {
ec_scalar c, r;
friend class crypto_ops;
};

POD_CLASS view_tag {
char data;
};
#pragma pack(pop)

static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 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();
Expand Down Expand Up @@ -88,6 +92,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 &);
};

/* Generate a value filled with random bytes.
Expand Down Expand Up @@ -179,8 +185,17 @@ namespace crypto {
const signature *sig) {
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);
}
}

CRYPTO_MAKE_COMPARABLE(public_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)
CRYPTO_MAKE_COMPARABLE(view_tag)
2 changes: 2 additions & 0 deletions src/cryptonote_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@

#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12

#define HF_VERSION_VIEW_TAGS 15
22 changes: 13 additions & 9 deletions src/cryptonote_core/blockchain_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,15 +624,16 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
uint8_t hf_version = b.major_version;
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) <<
", cumulative size " << cumulative_size);
#endif
for (size_t try_count = 0; try_count != 10; ++try_count) {
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, hf_version);

CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
Expand Down Expand Up @@ -905,15 +906,16 @@ bool blockchain_storage::add_out_to_get_random_outs(std::vector<std::pair<crypto
CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index="
<< amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first);
transaction& tx = tx_it->second.tx;
CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type");
crypto::public_key output_public_key;
CHECK_AND_ASSERT_MES(get_output_public_key(tx.vout[amount_outs[i].second], output_public_key), false, "failed to get tx output public key");

//check if transaction is unlocked
if(!is_tx_spendtime_unlocked(tx.unlock_time))
return false;

COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.global_amount_index = i;
oen.out_key = boost::get<txout_to_key>(tx.vout[amount_outs[i].second].target).key;
oen.out_key = output_public_key;
return true;
}
//------------------------------------------------------------------
Expand Down Expand Up @@ -1191,8 +1193,9 @@ bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>
auto tx_it = m_transactions.find(out_entry.first);
CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index");
CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size");
CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size");
pkeys.push_back(boost::get<txout_to_key>(tx_it->second.tx.vout[out_entry.second].target).key);
crypto::public_key output_public_key;
CHECK_AND_ASSERT_MES(get_output_public_key(tx_it->second.tx.vout[out_entry.second], output_public_key), false, "failed to get tx output public key");
pkeys.push_back(output_public_key);
}

return true;
Expand Down Expand Up @@ -1383,13 +1386,14 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h
return false;
}

if(out.target.type() != typeid(txout_to_key))
crypto::public_key output_public_key;
if(!get_output_public_key(out, output_public_key))
{
LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which());
LOG_PRINT_L0("Failed to get output public key");
return false;
}

m_results_collector.push_back(&boost::get<txout_to_key>(out.target).key);
m_results_collector.push_back(&output_public_key);
return true;
}
};
Expand Down
20 changes: 19 additions & 1 deletion src/cryptonote_core/cryptonote_basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,28 @@ namespace cryptonote
crypto::hash hash;
};

// outputs <= HF_VERSION_VIEW_TAGS
struct txout_to_key
{
txout_to_key() { }
txout_to_key(const crypto::public_key &_key) : key(_key) { }
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()
};

#pragma pack(push, 1)
struct bb_txout_to_key
{
Expand Down Expand Up @@ -134,7 +149,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 boost::variant<txout_to_script, txout_to_scripthash, bb_txout_to_key> bb_txout_target_v;

//typedef std::pair<uint64_t, txout> out_t;
Expand Down Expand Up @@ -603,6 +618,7 @@ 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::bb_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);

Expand All @@ -614,6 +630,7 @@ 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::bb_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");

Expand All @@ -625,5 +642,6 @@ 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::bb_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");
7 changes: 7 additions & 0 deletions src/cryptonote_core/cryptonote_boost_serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ namespace boost
a & x.mix_attr;
}

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)
{
Expand Down
67 changes: 54 additions & 13 deletions src/cryptonote_core/cryptonote_format_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs) {
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
tx.vin.clear();
tx.vout.clear();
tx.extra.clear();
Expand Down Expand Up @@ -104,12 +104,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);
}

Expand Down Expand Up @@ -477,13 +482,13 @@ namespace cryptonote
{
BOOST_FOREACH(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 in transaction id=" << get_transaction_hash(tx));

CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput 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;
Expand Down Expand Up @@ -527,6 +532,23 @@ 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;
}
//---------------------------------------------------------------
std::string short_hash_str(const crypto::hash& h)
{
std::string res = string_tools::pod_to_hex(h);
Expand All @@ -536,13 +558,31 @@ namespace cryptonote
return res;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index)
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 is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, size_t output_index)
{
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
crypto::public_key pk;
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
return pk == out_key.key;
return pk == output_public_key;
}
//---------------------------------------------------------------
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered)
Expand All @@ -559,8 +599,9 @@ namespace cryptonote
size_t i = 0;
BOOST_FOREACH(const tx_out& o, tx.vout)
{
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" );
if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i))
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" );
if(is_out_to_acc(acc, output_public_key, tx_pub_key, i))
{
outs.push_back(i);
money_transfered += o.amount;
Expand Down
6 changes: 4 additions & 2 deletions src/cryptonote_core/cryptonote_format_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace cryptonote
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1, uint8_t hf_version = 1);

struct tx_source_entry
{
Expand Down Expand Up @@ -62,9 +62,10 @@ namespace cryptonote
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id);
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 append_mm_tag_to_extra(std::vector<uint8_t>& tx_extra, const tx_extra_merge_mining_tag& mm_tag);
bool get_mm_tag_from_extra(const std::vector<uint8_t>& tx_extra, tx_extra_merge_mining_tag& mm_tag);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index);
bool is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, size_t output_index);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered);
bool 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);
Expand Down Expand Up @@ -93,6 +94,7 @@ namespace cryptonote
bool parse_and_validate_block_from_blob(const blobdata& b_blob, bb_block& b);
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);
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);
Expand Down
2 changes: 2 additions & 0 deletions src/serialization/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ 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");
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");

0 comments on commit 58fbe41

Please sign in to comment.