Merge pull request #29 from moneroexamples/refactor_viewkey

Decode mixins using viewkey and address
master
moneroexamples 8 years ago committed by GitHub
commit 22c09ab5d4

@ -109,33 +109,32 @@ namespace xmreg
bool bool
MicroCore::get_block_by_height(const uint64_t& height, block& blk) MicroCore::get_block_by_height(const uint64_t& height, block& blk)
{ {
crypto::hash block_id;
try try
{ {
block_id = m_blockchain_storage.get_block_id_by_height(height); blk = m_blockchain_storage.get_db().get_block_from_height(height);
} }
catch (const exception& e) catch (const BLOCK_DNE& e)
{ {
cerr << e.what() << endl; cerr << "Block of height " << height
<< " not found in the blockchain!"
<< e.what()
<< endl;
return false; return false;
} }
catch (const DB_ERROR& e)
if (!m_blockchain_storage.have_block(block_id))
{ {
cerr << "Block with hash " << block_id cerr << "Blockchain access error when getting block " << height
<< " not found in the blockchain!" << e.what()
<< endl; << endl;
return false; return false;
} }
catch (...)
if (!m_blockchain_storage.get_block_by_hash(block_id, blk))
{ {
cerr << "Block with hash " << block_id cerr << "Something went terribly wrong when getting block " << height
<< "and height " << height << " not found!"
<< endl; << endl;
return false; return false;
} }

@ -1283,27 +1283,28 @@ public:
// if mine output has RingCT, i.e., tx version is 2 // if mine output has RingCT, i.e., tx version is 2
if (mine_output && tx.version == 2) if (mine_output && tx.version == 2)
{ {
// initialize with regular amount
uint64_t rct_amount = money_transfered[output_idx];
bool r;
r = decode_ringct(tx.rct_signatures,
pub_key,
prv_view_key,
output_idx,
tx.rct_signatures.ecdhInfo[output_idx].mask,
rct_amount);
if (!r)
{
cerr << "\nshow_my_outputs: Cant decode ringCT! " << endl;
}
// cointbase txs have amounts in plain sight. // cointbase txs have amounts in plain sight.
// so use amount from ringct, only for non-coinbase txs // so use amount from ringct, only for non-coinbase txs
if (!is_coinbase(tx)) if (!is_coinbase(tx))
{ {
// initialize with regular amount
uint64_t rct_amount = money_transfered[output_idx];
bool r;
r = decode_ringct(tx.rct_signatures,
pub_key,
prv_view_key,
output_idx,
tx.rct_signatures.ecdhInfo[output_idx].mask,
rct_amount);
if (!r)
{
cerr << "\nshow_my_outputs: Cant decode ringCT! " << endl;
}
outp.second = rct_amount; outp.second = rct_amount;
money_transfered[output_idx] = rct_amount; money_transfered[output_idx] = rct_amount;
} }
@ -1330,28 +1331,20 @@ public:
// we can also test ouputs used in mixins for key images // we can also test ouputs used in mixins for key images
// this can show possible spending. Only possible, because // this can show possible spending. Only possible, because
// without a spend key, we cant know for sure. It might be // without a spend key, we cant know for sure. It might be
// that our output was used by someelse for their mixin. // that our output was used by someone else for their mixins.
// for this we can look in our custom db, for efficiencly // For this we can look in our custom db, for efficiencly
unique_ptr<xmreg::MyLMDB> mylmdb;
if (bf::is_directory(lmdb2_path)) bool show_key_images {false};
{
mylmdb = make_unique<xmreg::MyLMDB>(lmdb2_path);
}
mstch::array inputs; mstch::array inputs;
vector<txin_to_key> input_key_imgs = xmreg::get_key_images(tx); vector<txin_to_key> input_key_imgs = xmreg::get_key_images(tx);
uint64_t sum_mixin_xmr {0};
for (const txin_to_key& in_key: input_key_imgs) for (const txin_to_key& in_key: input_key_imgs)
{ {
if (!mylmdb)
{
break;
}
// get absolute offsets of mixins // get absolute offsets of mixins
std::vector<uint64_t> absolute_offsets std::vector<uint64_t> absolute_offsets
= cryptonote::relative_output_offsets_to_absolute( = cryptonote::relative_output_offsets_to_absolute(
@ -1386,113 +1379,145 @@ public:
// to store our mixins found for the given key image // to store our mixins found for the given key image
vector<map<string, string>> our_mixins_found; vector<map<string, string>> our_mixins_found;
// for each found output public key find check if its ours or not // mixin counter
for (const cryptonote::output_data_t& output_data: mixin_outputs) size_t count = 0;
// for each found output public key check if its ours or not
//for (const cryptonote::output_data_t& output_data: mixin_outputs)
for (const uint64_t& abs_offset: absolute_offsets)
{ {
// get basic information about mixn's output
cryptonote::output_data_t output_data = mixin_outputs.at(count);
tx_out_index tx_out_idx;
try
{
// get pair pair<crypto::hash, uint64_t> where first is tx hash
// and second is local index of the output i in that tx
tx_out_idx = core_storage->get_db()
.get_output_tx_and_index(in_key.amount, abs_offset);
}
catch (const OUTPUT_DNE& e)
{
string out_msg = fmt::format(
"Output with amount {:d} and index {:d} does not exist!",
in_key.amount, abs_offset
);
cerr << out_msg << endl;
break;
}
string out_pub_key_str = pod_to_hex(output_data.pubkey); string out_pub_key_str = pod_to_hex(output_data.pubkey);
//cout << "out_pub_key_str: " << out_pub_key_str << endl; //cout << "out_pub_key_str: " << out_pub_key_str << endl;
// this will be txs where the outputs come from
vector<string> found_tx_hashes;
// get mixin transaction
transaction mixin_tx;
mylmdb->search(out_pub_key_str, if (!mcore->get_tx(tx_out_idx.first, mixin_tx))
found_tx_hashes, {
"output_public_keys"); cerr << "Cant get tx: " << tx_out_idx.first << endl;
break;
}
mixins.push_back(mstch::map{ mixins.push_back(mstch::map{
{"mixin_pub_key", out_pub_key_str}, {"mixin_pub_key" , out_pub_key_str},
make_pair(string("mixin_outputs"), mstch::array{}) {"mixin_outputs" , mstch::array{}},
{"has_mixin_outputs" , false}
}); });
mstch::array& mixin_outputs = boost::get<mstch::array>( mstch::array& mixin_outputs = boost::get<mstch::array>(
boost::get<mstch::map>(mixins.back())["mixin_outputs"] boost::get<mstch::map>(mixins.back())["mixin_outputs"]
); );
// for each output transaction, check if its ours mstch::node& has_mixin_outputs
// as before = boost::get<mstch::map>(mixins.back())["has_mixin_outputs"];
for (string tx_hash_str: found_tx_hashes)
{
crypto::hash tx_hash;
hex_to_pod(tx_hash_str, tx_hash); bool found_something {false};
transaction mixin_tx;
if (!mcore->get_tx(tx_hash, mixin_tx))
{
cerr << "Cant get tx in blockchain: " << tx_hash << endl;
continue;
}
public_key mixin_tx_pub_key
= xmreg::get_tx_pub_key_from_received_outs(mixin_tx);
public_key tx_pub_key string mixin_tx_pub_key_str = pod_to_hex(mixin_tx_pub_key);
= xmreg::get_tx_pub_key_from_received_outs(mixin_tx);
// public transaction key is combined with our viewkey // public transaction key is combined with our viewkey
// to create, so called, derived key. // to create, so called, derived key.
key_derivation derivation; key_derivation derivation;
if (!generate_key_derivation(tx_pub_key, prv_view_key, derivation)) if (!generate_key_derivation(mixin_tx_pub_key, prv_view_key, derivation))
{ {
cerr << "Cant get derived key for: " << "\n" cerr << "Cant get derived key for: " << "\n"
<< "pub_tx_key: " << tx_pub_key << " and " << "pub_tx_key: " << mixin_tx_pub_key << " and "
<< "prv_view_key" << prv_view_key << endl; << "prv_view_key" << prv_view_key << endl;
continue; continue;
} }
// <public_key , amount , out idx> // <public_key , amount , out idx>
vector<tuple<txout_to_key, uint64_t, uint64_t>> output_pub_keys; vector<tuple<txout_to_key, uint64_t, uint64_t>> output_pub_keys;
output_pub_keys = xmreg::get_ouputs_tuple(mixin_tx); output_pub_keys = xmreg::get_ouputs_tuple(mixin_tx);
mixin_outputs.push_back(mstch::map{ mixin_outputs.push_back(mstch::map{
{"mix_tx_hash" , tx_hash_str}, {"mix_tx_hash" , tx_hash_str},
make_pair(string("found_outputs"), mstch::array{}) {"mix_tx_pub_key" , mixin_tx_pub_key_str},
}); {"found_outputs" , mstch::array{}},
{"has_found_outputs", false}
});
mstch::array& found_outputs = boost::get<mstch::array>( mstch::array& found_outputs = boost::get<mstch::array>(
boost::get<mstch::map>(mixin_outputs.back())["found_outputs"] boost::get<mstch::map>(mixin_outputs.back())["found_outputs"]
); );
mstch::node& has_found_outputs
= boost::get<mstch::map>(mixin_outputs.back())["has_found_outputs"];
// for each output in mixin tx, find the one from key_image // for each output in mixin tx, find the one from key_image
// and check if its ours. // and check if its ours.
for (const auto& mix_out: output_pub_keys) for (const auto& mix_out: output_pub_keys)
{ {
txout_to_key txout_k = std::get<0>(mix_out); txout_to_key txout_k = std::get<0>(mix_out);
uint64_t amount = std::get<1>(mix_out); uint64_t amount = std::get<1>(mix_out);
uint64_t output_idx_in_tx = std::get<2>(mix_out); uint64_t output_idx_in_tx = std::get<2>(mix_out);
//@todo fix this for loop //cout << " - " << pod_to_hex(txout_k.key) << endl;
continue;
// if (mix_out.first.key != output_data.pubkey) // // analyze only those output keys
// // that were used in mixins
// if (txout_k.key != output_data.pubkey)
// { // {
// continue; // continue;
// } // }
// get the tx output public key // get the tx output public key
// that normally would be generated for us, // that normally would be generated for us,
// if someone had sent us some xmr. // if someone had sent us some xmr.
public_key tx_pubkey_generated; public_key tx_pubkey_generated;
derive_public_key(derivation, derive_public_key(derivation,
output_idx_in_tx, output_idx_in_tx,
address.m_spend_public_key, address.m_spend_public_key,
tx_pubkey_generated); tx_pubkey_generated);
// check if generated public key matches the current output's key // check if generated public key matches the current output's key
bool mine_output = (txout_k.key == tx_pubkey_generated); bool mine_output = (txout_k.key == tx_pubkey_generated);
if (mine_output && mixin_tx.version == 2) if (mine_output && mixin_tx.version == 2)
{
// cointbase txs have amounts in plain sight.
// so use amount from ringct, only for non-coinbase txs
if (!is_coinbase(mixin_tx))
{ {
// initialize with regular amount // initialize with regular amount
uint64_t rct_amount = amount; uint64_t rct_amount = amount;
@ -1500,10 +1525,10 @@ public:
bool r; bool r;
r = decode_ringct(mixin_tx.rct_signatures, r = decode_ringct(mixin_tx.rct_signatures,
txout_k.key, mixin_tx_pub_key,
prv_view_key, prv_view_key,
output_idx_in_tx, output_idx_in_tx,
mixin_tx.rct_signatures.ecdhInfo[output_idx].mask, mixin_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask,
rct_amount); rct_amount);
if (!r) if (!r)
@ -1511,50 +1536,86 @@ public:
cerr << "show_my_outputs: key images: Cant decode ringCT!" << endl; cerr << "show_my_outputs: key images: Cant decode ringCT!" << endl;
} }
// cointbase txs have amounts in plain sight. amount = rct_amount;
// so use amount from ringct, only for non-coinbase txs
if (!is_coinbase(mixin_tx))
{
amount = rct_amount;
}
}
} // if (mine_output && mixin_tx.version == 2)
}
// save our mixnin's public keys
found_outputs.push_back(mstch::map {
{"my_public_key" , pod_to_hex(txout_k.key)},
{"tx_hash" , tx_hash_str},
{"mine_output" , mine_output},
{"out_idx" , to_string(output_idx_in_tx)},
{"formed_output_pk", out_pub_key_str},
{"amount" , xmreg::xmr_amount_to_str(amount)},
});
if (mine_output)
{
// save our mixnin's public keys
found_outputs.push_back(mstch::map {
{"my_public_key" , pod_to_hex(txout_k.key)},
{"tx_hash" , tx_hash_str},
{"mine_output" , mine_output},
{"out_idx" , to_string(output_idx_in_tx)},
{"formed_output_pk", out_pub_key_str},
{"out_in_match" , (amount == in_key.amount)},
{"amount" , xmreg::xmr_amount_to_str(amount)}
});
if (mine_output)
{
// cout << " - " << pod_to_hex(txout_k.key) // cout << " - " << pod_to_hex(txout_k.key)
// <<": " << mine_output << " amount: " // <<": " << mine_output << " amount: "
// << xmreg::xmr_amount_to_str(amount) // << xmreg::xmr_amount_to_str(amount)
// << endl; // << endl;
found_something = true;
show_key_images = true;
// for regular txs, just concentrated on outputs
// which have same amount as the key image.
// for ringct its not possible to know for sure amount
// in key image without spend key, so we just use all
if (mixin_tx.version < 2 && amount == in_key.amount)
{
sum_mixin_xmr += amount;
} }
else if (mixin_tx.version == 2) // ringct
{
sum_mixin_xmr += amount;
}
}
} // for (const pair<txout_to_key, uint64_t>& mix_out: txd.output_pub_keys)
} // for (const pair<txout_to_key, uint64_t>& mix_out: txd.output_pub_keys) has_found_outputs = !found_outputs.empty();
} // for (string tx_hash_str: found_tx_hashes) has_mixin_outputs = found_something;
} // for (const cryptonote::output_data_t& output_data: mixin_outputs) } // for (const cryptonote::output_data_t& output_data: mixin_outputs)
} // for (const txin_to_key& in_key: input_key_imgs) } // for (const txin_to_key& in_key: input_key_imgs)
context.emplace("outputs", outputs); context.emplace("outputs", outputs);
context["found_our_outputs"] = (sum_xmr > 0); context["found_our_outputs"] = (sum_xmr > 0);
context["sum_xmr"] = xmreg::xmr_amount_to_str(sum_xmr); context["sum_xmr"] = xmreg::xmr_amount_to_str(sum_xmr);
context.emplace("inputs", inputs); context.emplace("inputs", inputs);
context["show_inputs"] = false;
context["show_inputs"] = show_key_images;
context["inputs_no"] = inputs.size();
context["sum_mixin_xmr"] = xmreg::xmr_amount_to_str(sum_mixin_xmr);
uint64_t possible_spending {0};
if (sum_mixin_xmr > (sum_xmr + txd.fee))
{
// (outcoming - incoming) - fee
possible_spending = (sum_mixin_xmr - sum_xmr) - txd.fee;
}
context["possible_spending"] = xmreg::xmr_amount_to_str(
possible_spending, "{:0.12f}", false);
// read my_outputs.html // read my_outputs.html
string my_outputs_html = xmreg::read(TMPL_MY_OUTPUTS); string my_outputs_html = xmreg::read(TMPL_MY_OUTPUTS);
@ -2678,30 +2739,35 @@ public:
// seems we found our output. so now lets decode its amount // seems we found our output. so now lets decode its amount
// using ringct // using ringct
bool r; if (output_source_tx.version == 2
&& !is_coinbase(output_source_tx))
{
bool r;
r = decode_ringct(output_source_tx.rct_signatures, r = decode_ringct(output_source_tx.rct_signatures,
tx_pub_key, tx_pub_key,
prv_view_key, prv_view_key,
output_idx_in_tx, output_idx_in_tx,
output_source_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask, output_source_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask,
rct_amount); rct_amount);
if (!r) if (!r)
{ {
string error_msg = fmt::format( string error_msg = fmt::format(
"Cant decode ringCT for " "Cant decode ringCT for "
"pub_tx_key: {:s} " "pub_tx_key: {:s} "
"using prv_view_key: {:s}", "using prv_view_key: {:s}",
tx_pub_key, prv_view_key); tx_pub_key, prv_view_key);
context["has_error"] = true;
context["error_msg"] = error_msg;
context["has_error"] = true; return mstch::render(full_page, context);
context["error_msg"] = error_msg; }
return mstch::render(full_page, context); xmr_amount = rct_amount;
}
xmr_amount = rct_amount; } // if (output_source_tx.version == 2 && !is_coinbase(output_source_tx))
break; break;

@ -111,4 +111,31 @@ form {
[type=radio]:checked ~ label ~ .content { [type=radio]:checked ~ label ~ .content {
z-index: 1; z-index: 1;
}
input#toggle-1[type=checkbox] {
position: absolute;
top: -9999px;
left: -9999px;
}
label#show-decoded-inputs {
/*-webkit-appearance: push-button;*/
/*-moz-appearance: button;*/
display: inline-block;
/*margin: 60px 0 10px 0;*/
cursor: pointer;
background-color: black;;
color: white;
width: 100%;
text-align: center;
}
div#decoded-inputs{
display: none;
}
/* Toggled State */
input#toggle-1[type=checkbox]:checked ~ div#decoded-inputs {
display: block;
} }

@ -40,7 +40,7 @@
<div class="center"> <div class="center">
<table class="center" > <table class="center" >
<tr> <tr>
<td>Stealth address</td> <td>output public key</td>
<td>amount</td> <td>amount</td>
<td>output match?</td> <td>output match?</td>
</tr> </tr>
@ -63,7 +63,7 @@
</table> </table>
<h3> <h3>
Sum XMR from matched outputs: Sum XMR from matched outputs (i.e., incoming XMR):
{{#found_our_outputs}} {{#found_our_outputs}}
{{sum_xmr}} {{sum_xmr}}
{{/found_our_outputs}} {{/found_our_outputs}}
@ -75,43 +75,78 @@
</div> </div>
{{#show_inputs}} {{#show_inputs}}
<br/>
<label id="show-decoded-inputs" for="toggle-1">Show decoded inputs</label>
<input type="checkbox" id="toggle-1">
<hr> <div id="decoded-inputs">
<h3>Inputs ({{inputs_no}})</h3>
<h3>Inputs</h3>
<div class="center"> <div class="center">
{{#inputs}} {{#inputs}}
<h4>Key image: {{key_image}}, amount {{key_image_amount}}</h4> <h4>Key image: {{key_image}}, amount {{key_image_amount}}</h4>
{{#mixins}} {{#mixins}}
<h5>Mixin of pub key: {{mixin_pub_key}}</h5> {{#has_mixin_outputs}}
{{#mixin_outputs}} {{#mixin_outputs}}
<h5> <div class="center">
uses outputs from tx: <table class="center">
<a href="/tx/{{mix_tx_hash}}">{{mix_tx_hash}}</a> <tr>
</h5> <td style="text-align: center;">
{{#found_outputs}} Mixin {{mixin_pub_key}} might use your outputs
<br/>
<h6 style="font-family: 'Lucida Console', Monaco, monospace; font-size: 12px; font-weight: normal;"> from tx of public key: <a href="/tx/{{mix_tx_hash}}">{{mix_tx_pub_key}}</a>
Output public key: {{my_public_key}}, </td>
amount: {{amount}}, </tr>
is ours: {{#has_found_outputs}}
{{#mine_output}} <tr><td>
<span style="color: #008009;font-weight: bold">{{mine_output}}</span> <div class="center">
{{/mine_output}} <table class="center">
{{^mine_output}} <tr>
{{mine_output}} <td>output public key</td>
{{/mine_output}} <td>amount</td>
</h6> <td>output match?</td>
</tr>
{{/found_outputs}} {{#found_outputs}}
{{/mixin_outputs}} <tr>
<td>{{my_public_key}}</td>
<td>{{amount}}</td>
<td>
{{#mine_output}}
<span style="color: #008009;font-weight: bold">{{mine_output}}</span>{{#out_in_match}}*{{/out_in_match}}
{{/mine_output}}
{{^mine_output}}
{{mine_output}}
{{/mine_output}}
</td>
</tr>
{{/found_outputs}}
</table>
</div>
</td></tr>
{{/has_found_outputs}}
</table>
</div>
{{/mixin_outputs}}
{{/has_mixin_outputs}}
{{/mixins}} {{/mixins}}
{{/inputs}} {{/inputs}}
</div> </div>
<h3>
Sum XMR from matched mixin's outputs: {{sum_mixin_xmr}}
<br/>
<span style="font-size: 16px"> Possible spending is:
{{possible_spending}} (tx fee included)
</span>
<br/>
<span style="font-size: 14px">Note: without private spendkey,
it is impossible to know whether this is your real spending. <br/>
So do not take this number seriously.
It is probably totally wrong anyway.</span>
</h3>
</div>
{{/show_inputs}} {{/show_inputs}}
</div> </div>
<br/>

@ -245,14 +245,23 @@ parse(const std::string& str, string format="%Y-%m-%d %H:%M:%S");
static static
string string
xmr_amount_to_str(const uint64_t& xmr_amount, string _format="{:0.12f}") xmr_amount_to_str(const uint64_t& xmr_amount,
string _format="{:0.12f}",
bool zero_to_question_mark=true)
{ {
string amount_str = "?"; string amount_str = "?";
if (xmr_amount > 0) if (!zero_to_question_mark)
{ {
amount_str = fmt::format(_format, XMR_AMOUNT(xmr_amount)); amount_str = fmt::format(_format, XMR_AMOUNT(xmr_amount));
} }
else
{
if (xmr_amount > 0 && zero_to_question_mark == true)
{
amount_str = fmt::format(_format, XMR_AMOUNT(xmr_amount));
}
}
return amount_str; return amount_str;
} }

Loading…
Cancel
Save