cryptonote: sort tx_extra fields

This removes some small amount of fingerprinting entropy.
There is no consensus rule to require this since this field
is technically free form, and a transaction is free to have
custom data in it.
This commit is contained in:
moneromooo-monero 2018-08-03 10:21:08 +00:00
parent 91c7d68b2d
commit 9907ea0694
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
5 changed files with 177 additions and 1 deletions

View file

@ -47,7 +47,6 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "misc_language.h"
#include "tx_extra.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"

View file

@ -376,6 +376,91 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
template<typename T>
static bool pick(binary_archive<true> &ar, std::vector<tx_extra_field> &fields, uint8_t tag)
{
std::vector<tx_extra_field>::iterator it;
while ((it = std::find_if(fields.begin(), fields.end(), [](const tx_extra_field &f) { return f.type() == typeid(T); })) != fields.end())
{
bool r = ::do_serialize(ar, tag);
CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
r = ::do_serialize(ar, boost::get<T>(*it));
CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
fields.erase(it);
}
return true;
}
//---------------------------------------------------------------
bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial)
{
std::vector<tx_extra_field> tx_extra_fields;
if(tx_extra.empty())
{
sorted_tx_extra.clear();
return true;
}
std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size());
std::istringstream iss(extra_str);
binary_archive<false> ar(iss);
bool eof = false;
size_t processed = 0;
while (!eof)
{
tx_extra_field field;
bool r = ::do_serialize(ar, field);
if (!r)
{
MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
if (!allow_partial)
return false;
break;
}
tx_extra_fields.push_back(field);
processed = iss.tellg();
std::ios_base::iostate state = iss.rdstate();
eof = (EOF == iss.peek());
iss.clear(state);
}
if (!::serialization::check_stream_state(ar))
{
MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
if (!allow_partial)
return false;
}
MTRACE("Sorted " << processed << "/" << tx_extra.size());
std::ostringstream oss;
binary_archive<true> nar(oss);
// sort by:
if (!pick<tx_extra_pub_key>(nar, tx_extra_fields, TX_EXTRA_TAG_PUBKEY)) return false;
if (!pick<tx_extra_additional_pub_keys>(nar, tx_extra_fields, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS)) return false;
if (!pick<tx_extra_nonce>(nar, tx_extra_fields, TX_EXTRA_NONCE)) return false;
if (!pick<tx_extra_merge_mining_tag>(nar, tx_extra_fields, TX_EXTRA_MERGE_MINING_TAG)) return false;
if (!pick<tx_extra_mysterious_minergate>(nar, tx_extra_fields, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG)) return false;
if (!pick<tx_extra_padding>(nar, tx_extra_fields, TX_EXTRA_TAG_PADDING)) return false;
// if not empty, someone added a new type and did not add a case above
if (!tx_extra_fields.empty())
{
MERROR("tx_extra_fields not empty after sorting, someone forgot to add a case above");
return false;
}
std::string oss_str = oss.str();
if (allow_partial && processed < tx_extra.size())
{
MDEBUG("Appending unparsed data");
oss_str += std::string((const char*)tx_extra.data() + processed, tx_extra.size() - processed);
}
sorted_tx_extra = std::vector<uint8_t>(oss_str.begin(), oss_str.end());
return true;
}
//---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index)
{
std::vector<tx_extra_field> tx_extra_fields;

View file

@ -31,6 +31,7 @@
#pragma once
#include "blobdatatype.h"
#include "cryptonote_basic_impl.h"
#include "tx_extra.h"
#include "account.h"
#include "subaddress_index.h"
#include "include_base_utils.h"
@ -64,6 +65,7 @@ namespace cryptonote
}
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial = false);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0);