parent
44ed2893b9
commit
a59a2743ae
@ -1,281 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by mwo on 13/02/17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "OutputInputIdentification.h"
|
|
||||||
#include "src/UniversalIdentifier.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace xmreg
|
|
||||||
{
|
|
||||||
|
|
||||||
OutputInputIdentification::OutputInputIdentification(
|
|
||||||
const address_parse_info* _a,
|
|
||||||
const secret_key* _v,
|
|
||||||
const transaction* _tx,
|
|
||||||
crypto::hash const& _tx_hash,
|
|
||||||
bool is_coinbase)
|
|
||||||
{
|
|
||||||
address_info = _a;
|
|
||||||
viewkey = _v;
|
|
||||||
tx = _tx;
|
|
||||||
|
|
||||||
tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(*tx);
|
|
||||||
|
|
||||||
tx_is_coinbase = is_coinbase;
|
|
||||||
tx_hash = _tx_hash;
|
|
||||||
|
|
||||||
is_rct = (tx->version == 2);
|
|
||||||
|
|
||||||
if (is_rct)
|
|
||||||
{
|
|
||||||
rct_type = tx->rct_signatures.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!generate_key_derivation(tx_pub_key, *viewkey, derivation))
|
|
||||||
{
|
|
||||||
OMWARN << "Cant get derived key for tx: "
|
|
||||||
<< pod_to_hex(tx_hash)
|
|
||||||
<< ", pub_tx_key: "
|
|
||||||
<< pod_to_hex(tx_pub_key);
|
|
||||||
|
|
||||||
static_assert(sizeof(derivation) == sizeof(rct::key),
|
|
||||||
"Mismatched sizes of key_derivation and rct::key");
|
|
||||||
|
|
||||||
// use identity derivation instead
|
|
||||||
// solution based on that found in wallet2.cpp in monero
|
|
||||||
// this will cause the tx output to be effectively skipped
|
|
||||||
memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
|
||||||
OutputInputIdentification::get_mixin_no()
|
|
||||||
{
|
|
||||||
if (mixin_no == 0 && !tx_is_coinbase)
|
|
||||||
mixin_no = xmreg::get_mixin_no(*tx);
|
|
||||||
|
|
||||||
return mixin_no;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OutputInputIdentification::identify_outputs()
|
|
||||||
{
|
|
||||||
// <public_key , amount , out idx>
|
|
||||||
vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs
|
|
||||||
= get_ouputs_tuple(*tx);
|
|
||||||
|
|
||||||
for (auto& out: outputs)
|
|
||||||
{
|
|
||||||
txout_to_key const& txout_k = std::get<0>(out);
|
|
||||||
uint64_t amount = std::get<1>(out);
|
|
||||||
uint64_t output_idx_in_tx = std::get<2>(out);
|
|
||||||
|
|
||||||
// get the tx output public key
|
|
||||||
// that normally would be generated for us,
|
|
||||||
// if someone had sent us some xmr.
|
|
||||||
public_key generated_tx_pubkey;
|
|
||||||
|
|
||||||
derive_public_key(derivation,
|
|
||||||
output_idx_in_tx,
|
|
||||||
address_info->address.m_spend_public_key,
|
|
||||||
generated_tx_pubkey);
|
|
||||||
|
|
||||||
// check if generated public key matches the current output's key
|
|
||||||
bool mine_output = (txout_k.key == generated_tx_pubkey);
|
|
||||||
|
|
||||||
// placeholder variable for ringct outputs info
|
|
||||||
// that we need to save in database
|
|
||||||
string rtc_outpk;
|
|
||||||
string rtc_mask;
|
|
||||||
string rtc_amount;
|
|
||||||
|
|
||||||
// if mine output has RingCT, i.e., tx version is 2
|
|
||||||
// need to decode its amount. otherwise its zero.
|
|
||||||
if (mine_output && tx->version == 2)
|
|
||||||
{
|
|
||||||
// initialize with regular amount value
|
|
||||||
// for ringct, except coinbase, it will be 0
|
|
||||||
uint64_t rct_amount_val = amount;
|
|
||||||
|
|
||||||
// cointbase txs have amounts in plain sight.
|
|
||||||
// so use amount from ringct, only for non-coinbase txs
|
|
||||||
if (!tx_is_coinbase)
|
|
||||||
{
|
|
||||||
bool r;
|
|
||||||
|
|
||||||
// for ringct non-coinbase txs, these values are given
|
|
||||||
// with txs.
|
|
||||||
// coinbase ringctx dont have this information. we will provide
|
|
||||||
// them only when needed, in get_unspent_outputs. So go there
|
|
||||||
// to see how we deal with ringct coinbase txs when we spent
|
|
||||||
// them
|
|
||||||
// go to CurrentBlockchainStatus::construct_output_rct_field
|
|
||||||
// to see how we deal with coinbase ringct that are used
|
|
||||||
// as mixins
|
|
||||||
rtc_outpk = pod_to_hex(tx->rct_signatures
|
|
||||||
.outPk[output_idx_in_tx].mask);
|
|
||||||
rtc_mask = pod_to_hex(tx->rct_signatures
|
|
||||||
.ecdhInfo[output_idx_in_tx].mask);
|
|
||||||
rtc_amount = pod_to_hex(tx->rct_signatures
|
|
||||||
.ecdhInfo[output_idx_in_tx].amount);
|
|
||||||
|
|
||||||
rct::key mask = tx->rct_signatures
|
|
||||||
.ecdhInfo[output_idx_in_tx].mask;
|
|
||||||
|
|
||||||
r = decode_ringct(tx->rct_signatures,
|
|
||||||
tx_pub_key,
|
|
||||||
*viewkey,
|
|
||||||
output_idx_in_tx,
|
|
||||||
mask,
|
|
||||||
rct_amount_val);
|
|
||||||
|
|
||||||
if (!r)
|
|
||||||
{
|
|
||||||
OMERROR << "Cant decode ringCT!";
|
|
||||||
throw OutputInputIdentificationException(
|
|
||||||
"Cant decode ringCT!");
|
|
||||||
}
|
|
||||||
|
|
||||||
amount = rct_amount_val;
|
|
||||||
|
|
||||||
} // if (!tx_is_coinbase)
|
|
||||||
|
|
||||||
} // if (mine_output && tx.version == 2)
|
|
||||||
|
|
||||||
if (mine_output)
|
|
||||||
{
|
|
||||||
total_received += amount;
|
|
||||||
|
|
||||||
identified_outputs.emplace_back(
|
|
||||||
output_info{
|
|
||||||
txout_k.key, amount, output_idx_in_tx,
|
|
||||||
rtc_outpk, rtc_mask, rtc_amount
|
|
||||||
});
|
|
||||||
|
|
||||||
} // if (mine_output)
|
|
||||||
|
|
||||||
} // for (const auto& out: outputs)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
OutputInputIdentification::identify_inputs(
|
|
||||||
unordered_map<public_key, uint64_t> const& known_outputs_keys,
|
|
||||||
CurrentBlockchainStatus* current_bc_status)
|
|
||||||
{
|
|
||||||
vector<txin_to_key> input_key_imgs = xmreg::get_key_images(*tx);
|
|
||||||
|
|
||||||
size_t search_misses {0};
|
|
||||||
|
|
||||||
// make timescale maps for mixins in input
|
|
||||||
for (txin_to_key const& in_key: input_key_imgs)
|
|
||||||
{
|
|
||||||
// get absolute offsets of mixins
|
|
||||||
std::vector<uint64_t> absolute_offsets
|
|
||||||
= cryptonote::relative_output_offsets_to_absolute(
|
|
||||||
in_key.key_offsets);
|
|
||||||
|
|
||||||
// get public keys of outputs used in the mixins that
|
|
||||||
// match to the offests
|
|
||||||
std::vector<cryptonote::output_data_t> mixin_outputs;
|
|
||||||
|
|
||||||
if (!current_bc_status->get_output_keys(in_key.amount,
|
|
||||||
absolute_offsets,
|
|
||||||
mixin_outputs))
|
|
||||||
{
|
|
||||||
OMERROR << "Mixins key images not found";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// indicates whether we found any matching mixin in the current input
|
|
||||||
bool found_a_match {false};
|
|
||||||
|
|
||||||
// for each found output public key check if its ours or not
|
|
||||||
for (size_t count = 0; count < absolute_offsets.size(); ++count)
|
|
||||||
{
|
|
||||||
// get basic information about mixn's output
|
|
||||||
cryptonote::output_data_t const& output_data
|
|
||||||
= mixin_outputs[count];
|
|
||||||
|
|
||||||
//cout << " - output_public_key_str: "
|
|
||||||
// << output_public_key_str;
|
|
||||||
|
|
||||||
// before going to the mysql, check our known outputs cash
|
|
||||||
// if the key exists. Its much faster than going to mysql
|
|
||||||
// for this.
|
|
||||||
|
|
||||||
auto it = known_outputs_keys.find(output_data.pubkey);
|
|
||||||
|
|
||||||
if (it != known_outputs_keys.end())
|
|
||||||
{
|
|
||||||
// this seems to be our mixin.
|
|
||||||
// save it into identified_inputs vector
|
|
||||||
|
|
||||||
identified_inputs.push_back(input_info {
|
|
||||||
pod_to_hex(in_key.k_image),
|
|
||||||
it->second, // amount
|
|
||||||
output_data.pubkey});
|
|
||||||
|
|
||||||
//cout << "\n\n" << it->second << endl;
|
|
||||||
|
|
||||||
found_a_match = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // for (const cryptonote::output_data_t& output_data: outputs)
|
|
||||||
|
|
||||||
if (found_a_match == false)
|
|
||||||
{
|
|
||||||
// if we didnt find any match, break of the look.
|
|
||||||
// there is no reason to check remaining key images
|
|
||||||
// as when we spent something, our outputs should be
|
|
||||||
// in all inputs in a given txs. Thus, if a single input
|
|
||||||
// is without our output, we can assume this tx does
|
|
||||||
// not contain any of our spendings.
|
|
||||||
|
|
||||||
// just to be sure before we break out of this loop,
|
|
||||||
// do it only after two misses
|
|
||||||
|
|
||||||
if (++search_misses > 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // for (txin_to_key const& in_key: input_key_imgs)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string const&
|
|
||||||
OutputInputIdentification::get_tx_hash_str()
|
|
||||||
{
|
|
||||||
if (tx_hash_str.empty())
|
|
||||||
tx_hash_str = pod_to_hex(tx_hash);
|
|
||||||
|
|
||||||
return tx_hash_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string const&
|
|
||||||
OutputInputIdentification::get_tx_prefix_hash_str()
|
|
||||||
{
|
|
||||||
if (tx_prefix_hash_str.empty())
|
|
||||||
{
|
|
||||||
tx_prefix_hash = get_transaction_prefix_hash(*tx);
|
|
||||||
tx_prefix_hash_str = pod_to_hex(tx_prefix_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx_prefix_hash_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
string const&
|
|
||||||
OutputInputIdentification::get_tx_pub_key_str()
|
|
||||||
{
|
|
||||||
if (tx_pub_key_str.empty())
|
|
||||||
tx_pub_key_str = pod_to_hex(tx_pub_key);
|
|
||||||
|
|
||||||
return tx_pub_key_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,248 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by mwo on 13/02/17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CurrentBlockchainStatus.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace xmreg
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
class OutputInputIdentificationException: public std::runtime_error
|
|
||||||
{
|
|
||||||
using std::runtime_error::runtime_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class takes user address, viewkey
|
|
||||||
* and searches for outputs in a given tx
|
|
||||||
* and possible spendings/inputs.
|
|
||||||
*
|
|
||||||
* It is the key class for identification of incoming monero
|
|
||||||
* and candidate outcoming xmr for a user in a blockchain and in
|
|
||||||
* mempool.
|
|
||||||
*
|
|
||||||
* searching for our incoming and outgoing xmr has two components.
|
|
||||||
*
|
|
||||||
* FIRST. to search for the incoming xmr, we use address, viewkey and
|
|
||||||
* outputs public key. Its straight forward, as this is what viewkey was
|
|
||||||
* designed to do.
|
|
||||||
*
|
|
||||||
* SECOND. Searching for spendings (i.e., key images) is more difficult,
|
|
||||||
* because we dont have spendkey. But what we can do is, we can look for
|
|
||||||
* candidate key images. And this can be achieved by checking if any mixin
|
|
||||||
* in associated with the given key image, is our output. If it is our output,
|
|
||||||
* then we assume its our key image (i.e. we spend this output). Off course
|
|
||||||
* this is only
|
|
||||||
* assumption as our outputs can be used in key images of others for their
|
|
||||||
* mixin purposes. Thus, we sent to the frontend the list of key images
|
|
||||||
* that we think are yours, and the frontend, because it has spendkey,
|
|
||||||
* can filter out false positives.
|
|
||||||
*/
|
|
||||||
class OutputInputIdentification
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// define a structure to keep information about found
|
|
||||||
// outputs that we can need in later parts.
|
|
||||||
struct output_info
|
|
||||||
{
|
|
||||||
public_key pub_key;
|
|
||||||
uint64_t amount;
|
|
||||||
uint64_t idx_in_tx;
|
|
||||||
string rtc_outpk;
|
|
||||||
string rtc_mask;
|
|
||||||
string rtc_amount;
|
|
||||||
};
|
|
||||||
|
|
||||||
// define a structure to keep information about found
|
|
||||||
// inputs that we can need in later parts.
|
|
||||||
struct input_info
|
|
||||||
{
|
|
||||||
string key_img;
|
|
||||||
uint64_t amount;
|
|
||||||
public_key out_pub_key;
|
|
||||||
};
|
|
||||||
|
|
||||||
crypto::hash tx_hash;
|
|
||||||
crypto::hash tx_prefix_hash;
|
|
||||||
public_key tx_pub_key;
|
|
||||||
|
|
||||||
string tx_hash_str;
|
|
||||||
string tx_prefix_hash_str;
|
|
||||||
string tx_pub_key_str;
|
|
||||||
|
|
||||||
bool tx_is_coinbase;
|
|
||||||
|
|
||||||
bool is_rct;
|
|
||||||
uint8_t rct_type;
|
|
||||||
|
|
||||||
uint64_t total_received {0};
|
|
||||||
|
|
||||||
uint64_t mixin_no {0};
|
|
||||||
|
|
||||||
// for each output, in a tx, check if it belongs
|
|
||||||
// to the given account of specific address and viewkey
|
|
||||||
|
|
||||||
// public transaction key is combined with our viewkey
|
|
||||||
// to create, so called, derived key.
|
|
||||||
key_derivation derivation;
|
|
||||||
|
|
||||||
|
|
||||||
vector<output_info> identified_outputs;
|
|
||||||
vector<input_info> identified_inputs;
|
|
||||||
|
|
||||||
// default constructor. Useful for unit tests
|
|
||||||
OutputInputIdentification() = default;
|
|
||||||
|
|
||||||
OutputInputIdentification(const address_parse_info* _a,
|
|
||||||
const secret_key* _v,
|
|
||||||
const transaction* _tx,
|
|
||||||
crypto::hash const& _tx_hash,
|
|
||||||
bool is_coinbase);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FIRST step. search for the incoming xmr using address, viewkey and
|
|
||||||
* outputs public keys.
|
|
||||||
*/
|
|
||||||
virtual void
|
|
||||||
identify_outputs();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECOND step. search for possible our inputs.
|
|
||||||
* We do this by comparing mixin public keys with
|
|
||||||
* our known output keys. If a metch is found, we assume
|
|
||||||
* that associated input is ours
|
|
||||||
*
|
|
||||||
* The known_outputs_keys parameter is optional. But when we have
|
|
||||||
* it, the identification is faster as we just check mixins public keys
|
|
||||||
* if they are in known_outputs_keys.
|
|
||||||
*
|
|
||||||
* searching without known_outputs_keys is not implemented yet here
|
|
||||||
* but it was done for the onion explorer. so later basically just
|
|
||||||
* copy and past here.
|
|
||||||
*
|
|
||||||
* known_outputs_keys is pair of <output public key, output amount>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
virtual void
|
|
||||||
identify_inputs(
|
|
||||||
unordered_map<public_key, uint64_t> const& known_outputs_keys,
|
|
||||||
CurrentBlockchainStatus* current_bc_status);
|
|
||||||
|
|
||||||
virtual string const&
|
|
||||||
get_tx_hash_str();
|
|
||||||
|
|
||||||
virtual string const&
|
|
||||||
get_tx_prefix_hash_str();
|
|
||||||
|
|
||||||
virtual string const&
|
|
||||||
get_tx_pub_key_str();
|
|
||||||
|
|
||||||
virtual uint64_t
|
|
||||||
get_mixin_no();
|
|
||||||
|
|
||||||
virtual ~OutputInputIdentification() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// address and viewkey for this search thread.
|
|
||||||
const address_parse_info* address_info;
|
|
||||||
const secret_key* viewkey;
|
|
||||||
|
|
||||||
// transaction that is beeing search
|
|
||||||
const transaction* tx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//class BaseIdentifier {
|
|
||||||
|
|
||||||
//public:
|
|
||||||
// BaseIdentifier(address_parse_info const* _a,
|
|
||||||
// const secret_key* _v,
|
|
||||||
// const transaction* _tx)
|
|
||||||
// : address_info {_a}, viewkey {_v}, tx {_tx}
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// virtual void identify() = 0;
|
|
||||||
|
|
||||||
//protected:
|
|
||||||
|
|
||||||
// // address and viewkey for this search thread.
|
|
||||||
// address_parse_info const* address_info;
|
|
||||||
// secret_key const* viewkey;
|
|
||||||
|
|
||||||
// // transaction that is beeing search
|
|
||||||
// transaction const* tx;
|
|
||||||
|
|
||||||
//};
|
|
||||||
|
|
||||||
//class OutputIdentifier : public BaseIdentifier
|
|
||||||
//{
|
|
||||||
// using BaseIdentifier::BaseIdentifier;
|
|
||||||
|
|
||||||
// using key_imgs_map = unordered_map<public_key, uint64_t>;
|
|
||||||
|
|
||||||
// // define a structure to keep information about found
|
|
||||||
// // outputs that we can need in later parts.
|
|
||||||
// struct output_info
|
|
||||||
// {
|
|
||||||
// public_key pub_key;
|
|
||||||
// uint64_t amount;
|
|
||||||
// uint64_t idx_in_tx;
|
|
||||||
// string rtc_outpk;
|
|
||||||
// string rtc_mask;
|
|
||||||
// string rtc_amount;
|
|
||||||
// };
|
|
||||||
|
|
||||||
//public:
|
|
||||||
|
|
||||||
// void
|
|
||||||
// set_known_outputs(key_imgs_map const* _known_outputs)
|
|
||||||
// {
|
|
||||||
// known_outputs_keys = _known_outputs;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void
|
|
||||||
// identify()
|
|
||||||
// {}
|
|
||||||
|
|
||||||
//private:
|
|
||||||
// key_imgs_map const* known_outputs_keys {nullptr};
|
|
||||||
//};
|
|
||||||
|
|
||||||
//class InpputIdentifier : public BaseIdentifier
|
|
||||||
//{
|
|
||||||
|
|
||||||
// using BaseIdentifier::BaseIdentifier;
|
|
||||||
|
|
||||||
// // define a structure to keep information about found
|
|
||||||
// // inputs that we can need in later parts.
|
|
||||||
// struct input_info
|
|
||||||
// {
|
|
||||||
// string key_img;
|
|
||||||
// uint64_t amount;
|
|
||||||
// public_key out_pub_key;
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
//public:
|
|
||||||
|
|
||||||
// void
|
|
||||||
// identify()
|
|
||||||
// {}
|
|
||||||
//};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue