Futher itegration of universal identifier

use_xmregcore
moneroexamples 5 years ago
parent 44ed2893b9
commit a59a2743ae

@ -3,7 +3,7 @@ var config = {
mainnetExplorerUrl: "https://xmrchain.com/",
testnetExplorerUrl: "https://testnet.xmrchain.com/",
stagenetExplorerUrl: "http://139.162.60.17:8082/",
nettype: 2, /* 0 - MAINNET, 1 - TESTNET, 2 - STAGENET */
nettype: 0, /* 0 - MAINNET, 1 - TESTNET, 2 - STAGENET */
coinUnitPlaces: 12,
txMinConfirms: 10, // corresponds to CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE in Monero
txCoinbaseMinConfirms: 60, // corresponds to CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW in Monero

@ -11,7 +11,6 @@ set(SOURCE_FILES
OpenMoneroRequests.cpp
TxSearch.cpp
RPCCalls.cpp
OutputInputIdentification.cpp
omversion.h.in
BlockchainSetup.cpp
ThreadRAII.cpp

@ -1236,5 +1236,39 @@ CurrentBlockchainStatus::get_txs_in_blocks(
return true;
}
MicroCoreAdapter::MicroCoreAdapter(CurrentBlockchainStatus* _cbs)
: cbs {_cbs}
{}
void
MicroCoreAdapter::get_output_key(uint64_t amount,
vector<uint64_t> const& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
const
{
cbs->get_output_keys(amount, absolute_offsets, outputs);
}
void
MicroCoreAdapter::get_output_tx_and_index(
uint64_t amount,
std::vector<uint64_t> const& offsets,
std::vector<tx_out_index>& indices)
const
{
cbs->get_output_tx_and_index(amount, offsets, indices);
}
bool
MicroCoreAdapter::get_tx(crypto::hash const& tx_hash, transaction& tx)
const
{
return cbs->get_tx(tx_hash, tx);
}
}

@ -359,5 +359,38 @@ protected:
};
// small adapter class that will anable using
// BlockchainCurrentStatus inside UniversalAdapter
// for locating inputs. We do this becasuse
// BlockchainCurrentStatus is using a thread pool
// to access MicroCore and blockchain. So we don't want
// to miss on that. Also UnversalAdapter for Inputs
// takes AbstractCore interface
class MicroCoreAdapter : public AbstractCore
{
public:
MicroCoreAdapter(CurrentBlockchainStatus* _cbs);
virtual void
get_output_key(uint64_t amount,
vector<uint64_t> const& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
const override;
virtual void
get_output_tx_and_index(
uint64_t amount,
std::vector<uint64_t> const& offsets,
std::vector<tx_out_index>& indices)
const override;
virtual bool
get_tx(crypto::hash const& tx_hash, transaction& tx)
const override;
private:
CurrentBlockchainStatus* cbs {};
};
}

@ -9,7 +9,6 @@
#include "src/UniversalIdentifier.hpp"
#include "db/ssqlses.h"
#include "OutputInputIdentification.h"
#include "version.h"
#include "../gen/omversion.h"
@ -1545,6 +1544,8 @@ OpenMoneroRequests::get_tx(
address_parse_info address_info;
secret_key viewkey;
MicroCoreAdapter mcore_addapter {current_bc_status.get()};
// to get info about recived xmr in this tx, we calculate it from
// scrach, i.e., search for outputs. We could get this info
// directly from the database, but doing it again here, is a good way
@ -1557,21 +1558,16 @@ OpenMoneroRequests::get_tx(
if (current_bc_status->get_xmr_address_viewkey(
xmr_address, address_info, viewkey))
{
OutputInputIdentification oi_identification {
&address_info, &viewkey, &tx, tx_hash,
coinbase};
oi_identification.identify_outputs();
auto identifier = make_identifier(tx,
make_unique<Output>(&address_info, &viewkey));
uint64_t total_received {0};
identifier.identify();
auto const& outputs_identified
= identifier.get<Output>()->get();
// we just get total amount recieved. we have viewkey,
// so this must be correct and front end does not
// need to do anything to check this.
for (auto& out_info: oi_identification.identified_outputs)
{
total_received += out_info.amount;
}
auto total_received = calc_total_xmr(outputs_identified);
j_response["total_received"] = std::to_string(total_received);
@ -1669,20 +1665,22 @@ OpenMoneroRequests::get_tx(
// Class that is resposnible for idenficitaction
// of our outputs
// and inputs in a given tx.
OutputInputIdentification oi_identification
{&address_info, &viewkey, &tx, tx_hash,
coinbase};
// no need mutex here, as this will be exectued only
// after the above. there is no threads here.
oi_identification.identify_inputs(known_outputs_keys,
current_bc_status.get());
auto identifier = make_identifier(tx,
make_unique<Input>(&address_info, &viewkey,
&known_outputs_keys,
&mcore_addapter));
identifier.identify();
auto const& inputs_identfied
= identifier.get<Input>()->get();
json j_spent_outputs = json::array();
uint64_t total_spent {0};
for (auto& in_info: oi_identification.identified_inputs)
for (auto& in_info: inputs_identfied)
{
// need to get output info from mysql, as we need
@ -1699,7 +1697,7 @@ OpenMoneroRequests::get_tx(
j_spent_outputs.push_back({
{"amount" , std::to_string(in_info.amount)},
{"key_image" , in_info.key_img},
{"key_image" , pod_to_hex(in_info.key_img)},
{"tx_pub_key" , out.tx_pub_key},
{"out_index" , out.out_index},
{"mixin" , out.mixin}});

@ -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()
// {}
//};
}

@ -18,49 +18,6 @@ namespace xmreg
// small adapter class that will anable using
// BlockchainCurrentStatus inside UniversalAdapter
// for locating inputs. We do this becasuse
// BlockchainCurrentStatus is using a thread pool
// to access MicroCore and blockchain. So we don't want
// to miss on that. Also UnversalAdapter for Inputs
// takes AbstractCore interface
class MicroCoreAdapter : public AbstractCore
{
public:
MicroCoreAdapter(CurrentBlockchainStatus* _cbs)
: cbs {_cbs}
{}
virtual void
get_output_key(uint64_t amount,
vector<uint64_t> const& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
const override
{
cbs->get_output_keys(amount, absolute_offsets, outputs);
}
virtual void
get_output_tx_and_index(
uint64_t amount,
std::vector<uint64_t> const& offsets,
std::vector<tx_out_index>& indices)
const override
{
cbs->get_output_tx_and_index(amount, offsets, indices);
}
virtual bool
get_tx(crypto::hash const& tx_hash, transaction& tx)
const override
{
return cbs->get_tx(tx_hash, tx);
}
private:
CurrentBlockchainStatus* cbs {};
};
TxSearch::TxSearch(XmrAccount& _acc,
std::shared_ptr<CurrentBlockchainStatus> _current_bc_status)

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save