diff --git a/main.cpp b/main.cpp index 8e81c94..c85b43a 100644 --- a/main.cpp +++ b/main.cpp @@ -198,6 +198,19 @@ int main(int ac, const char* av[]) { return xmrblocks.show_my_outputs(tx_hash, xmr_address, viewkey); }); + + CROW_ROUTE(app, "/prove").methods("GET"_method) + ([&](const crow::request& req) { + + string tx_hash = string(req.url_params.get("txhash")); + string tx_prv_key = string(req.url_params.get("txprvkey")); + string xmr_address = string(req.url_params.get("xmraddress")); + + return xmrblocks.show_prove(tx_hash, xmr_address, tx_prv_key); + }); + + + CROW_ROUTE(app, "/search").methods("GET"_method) ([&](const crow::request& req) { return xmrblocks.search(string(req.url_params.get("value"))); diff --git a/src/page.h b/src/page.h index 661a9bb..0077b08 100644 --- a/src/page.h +++ b/src/page.h @@ -1377,7 +1377,8 @@ namespace xmreg { {"has_payment_id" , txd.payment_id != null_hash}, {"has_payment_id8" , txd.payment_id8 != null_hash8}, {"payment_id" , pid_str}, - {"payment_id8" , pid8_str} + {"payment_id8" , pid8_str}, + {"tx_prove" , false} }; string server_time_str = xmreg::timestamp_to_str(server_timestamp, "%F"); @@ -1485,6 +1486,274 @@ namespace xmreg { return mstch::render(full_page, context); } + string + show_prove(string tx_hash_str, + string xmr_address_str, + string tx_prv_key_str) + { + + // remove white characters + boost::trim(tx_hash_str); + boost::trim(xmr_address_str); + boost::trim(tx_prv_key_str); + + if (tx_hash_str.empty()) + { + return string("tx hash/id not provided!"); + } + + if (xmr_address_str.empty()) + { + return string("Monero address not provided!"); + } + + if (tx_prv_key_str.empty()) + { + return string("Tx private key not provided!"); + } + + // parse tx hash string to hash object + crypto::hash tx_hash; + + if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) + { + cerr << "Cant parse tx hash: " << tx_hash_str << endl; + return string("Cant get tx hash due to parse error: " + tx_hash_str); + } + + // parse string representing given monero address + cryptonote::account_public_address address; + + if (!xmreg::parse_str_address(xmr_address_str, address, testnet)) + { + cerr << "Cant parse string address: " << xmr_address_str << endl; + return string("Cant parse xmr address: " + xmr_address_str); + } + + // parse string representing given private viewkey + crypto::secret_key tx_prv_key; + + if (!xmreg::parse_str_secret_key(tx_prv_key_str, tx_prv_key)) + { + cerr << "Cant parse view key: " << tx_prv_key_str << endl; + return string("Cant parse view key: " + tx_prv_key_str); + } + + // tx age + pair age; + + string blk_timestamp {"N/A"}; + + // get transaction + transaction tx; + + if (!mcore->get_tx(tx_hash, tx)) + { + cerr << "Cant get tx in blockchain: " << tx_hash + << ". \n Check mempool now" << endl; + + vector> found_txs + = search_mempool(tx_hash); + + if (!found_txs.empty()) + { + // there should be only one tx found + tx = found_txs.at(0).second; + + // since its tx in mempool, it has no blk yet + // so use its recive_time as timestamp to show + + uint64_t tx_recieve_timestamp + = found_txs.at(0).first.receive_time; + + blk_timestamp = xmreg::timestamp_to_str(tx_recieve_timestamp); + + age = get_age(server_timestamp, + tx_recieve_timestamp, + FULL_AGE_FORMAT); + } + else + { + // tx is nowhere to be found :-( + return string("Cant get tx: " + tx_hash_str); + } + } + + tx_details txd = get_tx_details(tx); + + uint64_t tx_blk_height {0}; + + bool tx_blk_found {false}; + + try + { + tx_blk_height = core_storage->get_db().get_tx_block_height(tx_hash); + tx_blk_found = true; + } + catch (exception& e) + { + cerr << "Cant get block height: " << tx_hash + << e.what() << endl; + } + + + // get block cointaining this tx + block blk; + + if (tx_blk_found && !mcore->get_block_by_height(tx_blk_height, blk)) + { + cerr << "Cant get block: " << tx_blk_height << endl; + } + + string tx_blk_height_str {"N/A"}; + + if (tx_blk_found) + { + // calculate difference between tx and server timestamps + age = get_age(server_timestamp, blk.timestamp, FULL_AGE_FORMAT); + + blk_timestamp = xmreg::timestamp_to_str(blk.timestamp); + + tx_blk_height_str = std::to_string(tx_blk_height); + } + + // payments id. both normal and encrypted (payment_id8) + string pid_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", txd.payment_id)); + string pid8_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", txd.payment_id8)); + + // initalise page tempate map with basic info about blockchain + mstch::map context { + {"testnet" , testnet}, + {"tx_hash" , tx_hash_str}, + {"xmr_address" , xmr_address_str}, + {"tx_prv_key" , REMOVE_HASH_BRAKETS( + fmt::format("{:s}", tx_prv_key))}, + {"tx_pub_key" , REMOVE_HASH_BRAKETS( + fmt::format("{:s}", txd.pk))}, + {"blk_height" , tx_blk_height_str}, + {"tx_size" , fmt::format("{:0.4f}", + static_cast(txd.size) / 1024.0)}, + {"tx_fee" , fmt::format("{:0.12f}", XMR_AMOUNT(txd.fee))}, + {"blk_timestamp" , blk_timestamp}, + {"delta_time" , age.first}, + {"outputs_no" , txd.output_pub_keys.size()}, + {"has_payment_id" , txd.payment_id != null_hash}, + {"has_payment_id8" , txd.payment_id8 != null_hash8}, + {"payment_id" , pid_str}, + {"payment_id8" , pid8_str}, + {"tx_prove" , true} + }; + + string server_time_str = xmreg::timestamp_to_str(server_timestamp, "%F"); + + uint64_t output_idx {0}; + + // public transaction key is combined with our viewkey + // to create, so called, derived key. + key_derivation derivation; + + if (!generate_key_derivation(address.m_view_public_key, + tx_prv_key, + derivation)) + { + cerr << "Cant get dervied key for: " << "\n" + << "pub_tx_key: " << txd.pk << " and " + << "tx_prv_key" << tx_prv_key << endl; + + return string("Cant get key_derivation"); + } + + + mstch::array outputs; + + uint64_t sum_xmr {0}; + + std::vector money_transfered(tx.vout.size()); + + std::deque mask(tx.vout.size()); + + uint64_t i {0}; + + for (pair& outp: txd.output_pub_keys) + { + + // get the tx output public key + // that normally would be generated for us, + // if someone had sent us some xmr. + public_key pubkey; + + derive_public_key(derivation, + output_idx, + address.m_spend_public_key, + pubkey); + + // check if generated public key matches the current output's key + bool mine_output = (outp.first.key == pubkey); + + // if mine output has RingCT, i.e., tx version is 2 + if (mine_output && tx.version == 2) + { + + uint64_t rct_amount {0}; + + bool r; + + r = decode_ringct(tx.rct_signatures, + txd.pk, + tx_prv_key, + i, + tx.rct_signatures.ecdhInfo[i].mask, + money_transfered[i]); + + if (!r) + { + cerr << "Cant decode ringCT!" << endl; + } + + outp.second = money_transfered[i]; + + cout << "i, money_transfered[i]" + << i << "," + << money_transfered[i] + << endl; + } + + if (mine_output) + { + sum_xmr += outp.second; + } + + outputs.push_back(mstch::map { + {"out_pub_key" , REMOVE_HASH_BRAKETS( + fmt::format("{:s}", + outp.first.key))}, + {"amount" , fmt::format("{:0.12f}", + XMR_AMOUNT(outp.second))}, + {"mine_output" , mine_output}, + {"output_idx" , fmt::format("{:02d}", output_idx++)} + }); + + ++i; + } + + cout << "outputs.size(): " << outputs.size() << endl; + + context["outputs"] = outputs; + context["sum_xmr"] = XMR_AMOUNT(sum_xmr); + + // read my_outputs.html + string my_outputs_html = xmreg::read(TMPL_MY_OUTPUTS); + + // add header and footer + string full_page = get_full_page(my_outputs_html); + + // render the page + return mstch::render(full_page, context); + } + + + + string search(string search_text) { diff --git a/src/templates/header.html b/src/templates/header.html index 33290ed..58109e5 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -78,6 +78,54 @@ box-shadow: 0 0 5px 1px #969696; } + + .tabs { + position: relative; + min-height: 200px; /* This part sucks */ + clear: both; + margin: 25px 0; + } + + .tab { + float: left; + } + + .tab label { + background: black; + padding: 10px; + border: 1px solid #ccc; + margin-left: -1px; + position: relative; + left: 1px; + } + + .tab [type=radio] { + display: none; + } + + .content { + position: absolute; + top: 28px; + left: 0; + background: black; + right: 0; + bottom: 0; + padding: 20px; + border: 1px solid #ccc; + } + + [type=radio]:checked ~ label { + background: #505050 ; + border-bottom: 1px solid white; + z-index: 2; + } + + [type=radio]:checked ~ label ~ .content { + z-index: 1; + } + + + diff --git a/src/templates/my_outputs.html b/src/templates/my_outputs.html index 5c22693..d8f3f9c 100644 --- a/src/templates/my_outputs.html +++ b/src/templates/my_outputs.html @@ -25,9 +25,16 @@ -

Checking which outputs belong to the given address and viewkey

-
address: {{xmr_address}}
-
viewkey: {{viewkey}}
+ {{^tx_prove}} +

Checking which outputs belong to the given address and viewkey

+
address: {{xmr_address}}
+
viewkey: {{viewkey}}
+ {{/tx_prove}} + {{#tx_prove}} +

Prove that you send this tx to the given address

+
address: {{xmr_address}}
+
Tx private key: {{tx_prv_key}}
+ {{/tx_prove}}

Outputs ({{outputs_no}})

diff --git a/src/templates/tx.html b/src/templates/tx.html index 96781e2..923f375 100644 --- a/src/templates/tx.html +++ b/src/templates/tx.html @@ -65,14 +65,44 @@
-

Decode the amounts of the above outputs which belong to the following address and viewkey

- -
-
-
-
- -
+
+
+ +
+ + +
+

Check which outputs belong to given Moenro address and viewkey

+
+
+
+
+ +
+ +
+
+ +
+ + + +
+

Prove to someone that you send them Monero in this transaction

+
Tx private key can be obtained using get_tx_key + command in monero-wallet-cli command line tool
+
+
+
+
+ +
+
+
+
+ +
+ {{#has_inputs}}