diff --git a/README.md b/README.md index 7ab49fa..1e35206 100755 --- a/README.md +++ b/README.md @@ -414,6 +414,31 @@ Output (only part shown): ``` +#### get_tx + +```bash +curl -w "\n" -X POST http://127.0.0.1:1984/get_tx -d '{"tx_hash": "bfbfbb3bfa169731a092891795be1c3c923a018882ac0efc0ed3e79e2d2b2e54"}' +``` + +Output (only part shown): + +```json +{ + "coinbase": false, + "error": "", + "fee": 22893920000, + "mixin_no": 11, + "no_confirmations": 2898, + "pub_key": "b753c863c64565ae81630bfdbf497f06955b6ce041f656565809d04be6ef9343", + "size": 13461, + "status": "OK", + "tx_hash": "bfbfbb3bfa169731a092891795be1c3c923a018882ac0efc0ed3e79e2d2b2e54", + "tx_height": 960491, + "xmr_inputs": 0, + "xmr_outputs": 0 +} +``` + #### get_unspent_outs ```bash diff --git a/html/index.html b/html/index.html index 9faf3fa..7dece73 100755 --- a/html/index.html +++ b/html/index.html @@ -65,6 +65,7 @@ + diff --git a/html/js/controllers/account_overview.js b/html/js/controllers/account_overview.js index 0f2590f..0b13970 100755 --- a/html/js/controllers/account_overview.js +++ b/html/js/controllers/account_overview.js @@ -26,7 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -thinwalletCtrls.controller('AccountOverviewCtrl', function ($scope, $rootScope, $http, $interval, AccountService, EVENT_CODES) { +thinwalletCtrls.controller('AccountOverviewCtrl', function ($scope, $rootScope, $http, + $interval, AccountService, + EVENT_CODES) { "use strict"; $rootScope.$watch('account', $scope.fetchTransactions); diff --git a/html/js/controllers/transaction_details.js b/html/js/controllers/transaction_details.js new file mode 100644 index 0000000..3a5ef66 --- /dev/null +++ b/html/js/controllers/transaction_details.js @@ -0,0 +1,14 @@ +/** + * Created by mwo on 02/08/17. + */ + + +thinwalletCtrls.controller('TransactionDetailsCtrl', function ($scope, $http, + AccountService, + ModalService, + ApiCalls) { + "use strict"; + + console.log("TransactionDetailsCtrl"); + +}); diff --git a/html/modals/transaction-details.html b/html/modals/transaction-details.html new file mode 100755 index 0000000..248ea6d --- /dev/null +++ b/html/modals/transaction-details.html @@ -0,0 +1,57 @@ +
+
+
+ +
+
+
+

Transaction details

+ +
+ +
+
{{transferConfirmDialog.address}}
+
+
+
+ +
{{transferConfirmDialog.amount | money}} (+{{transferConfirmDialog.fee | money}} fee)
+
+
+ +
{{transferConfirmDialog.mixin}}
+
+
+ +
{{transferConfirmDialog.priority}}
+
+
+
+
+ +
{{transferConfirmDialog.payment_id || "N/A"}}
+
+
+ +
{{transferConfirmDialog.txBlobKBytes}}
+
+
+ +
+
{{transferConfirmDialog.tx_hash}}
+
+ +
+
{{transferConfirmDialog.tx_prv_key}}
+
+
+
+
After pressing "Confirm", an attempt will be made to + submit this tx to a Monero node. The node will perform additional checks of the tx + (e.g., if the miner fee is sufficient), and if the tests pass, the tx will be submitted to + Monero network awaiting miners confirmation. +
+
+
+
+
+
diff --git a/main.cpp b/main.cpp index b25a417..fa473ba 100755 --- a/main.cpp +++ b/main.cpp @@ -162,6 +162,7 @@ MAKE_RESOURCE(get_random_outs); MAKE_RESOURCE(submit_raw_tx); MAKE_RESOURCE(import_wallet_request); MAKE_RESOURCE(import_recent_wallet_request); +MAKE_RESOURCE(get_tx); MAKE_RESOURCE(get_version); // restbed service @@ -176,6 +177,7 @@ service.publish(get_random_outs); service.publish(submit_raw_tx); service.publish(import_wallet_request); service.publish(import_recent_wallet_request); +service.publish(get_tx); service.publish(get_version); auto settings = make_shared( ); diff --git a/src/CurrentBlockchainStatus.cpp b/src/CurrentBlockchainStatus.cpp index 4be323f..a1557ac 100755 --- a/src/CurrentBlockchainStatus.cpp +++ b/src/CurrentBlockchainStatus.cpp @@ -247,7 +247,7 @@ CurrentBlockchainStatus::tx_exist(const string& tx_hash_str, uint64_t& tx_index) return tx_exist(tx_hash, tx_index); } - throw runtime_error("hex_to_pod(tx_hash_str, tx_hash) failed!"); + false; } bool @@ -863,8 +863,38 @@ CurrentBlockchainStatus::find_txs_in_mempool( return true; }; +bool +CurrentBlockchainStatus::get_tx(crypto::hash const& tx_hash, transaction& tx) +{ + return mcore->get_tx(tx_hash, tx); +} + + +bool +CurrentBlockchainStatus::get_tx(string const& tx_hash_str, transaction& tx) +{ + crypto::hash tx_hash; + + if (!hex_to_pod(tx_hash_str, tx_hash)) + { + return false; + } - bool + return mcore->get_tx(tx_hash, tx); +} + +bool +CurrentBlockchainStatus::get_tx_block_height(crypto::hash const& tx_hash, int64_t& tx_height) +{ + if (!tx_exist(tx_hash)) + return false; + + tx_height = core_storage->get_db().get_tx_block_height(tx_hash); + + return true; +} + +bool CurrentBlockchainStatus::set_new_searched_blk_no(const string& address, uint64_t new_value) { std::lock_guard lck (searching_threads_map_mtx); diff --git a/src/CurrentBlockchainStatus.h b/src/CurrentBlockchainStatus.h index 644e6ac..5906f0f 100755 --- a/src/CurrentBlockchainStatus.h +++ b/src/CurrentBlockchainStatus.h @@ -188,6 +188,15 @@ struct CurrentBlockchainStatus find_txs_in_mempool(const string& address_str, json& transactions); + static bool + get_tx(crypto::hash const& tx_hash, transaction& tx); + + static bool + get_tx(string const& tx_hash_str, transaction& tx); + + static bool + get_tx_block_height(crypto::hash const& tx_hash, int64_t& tx_height); + static bool set_new_searched_blk_no(const string& address, uint64_t new_value); diff --git a/src/YourMoneroRequests.cpp b/src/YourMoneroRequests.cpp index 73623cc..472b123 100755 --- a/src/YourMoneroRequests.cpp +++ b/src/YourMoneroRequests.cpp @@ -133,7 +133,7 @@ YourMoneroRequests::get_address_txs(const shared_ptr< Session > session, const B json j_response; json j_request; - vector requested_values{"address", "view_key"}; + vector requested_values {"address", "view_key"}; if (!parse_request(body, requested_values, j_request, j_response)) { @@ -993,6 +993,156 @@ YourMoneroRequests::import_recent_wallet_request(const shared_ptr< Session > ses } + +void +YourMoneroRequests::get_tx(const shared_ptr< Session > session, const Bytes & body) +{ + + json j_request = body_to_json(body); + + string tx_hash_str = j_request["tx_hash"]; + + json j_response; + + j_response["status"] = "error"; + j_response["error"] = "Some error occured"; + + + crypto::hash tx_hash; + + if (!hex_to_pod(tx_hash_str, tx_hash)) + { + cerr << "Cant parse tx hash! : " << tx_hash_str << '\n'; + j_response["status"] = "Cant parse tx hash! : " + tx_hash_str; + session_close(session, j_response.dump()); + return; + } + + + transaction tx; + + bool tx_found {false}; + + if (!CurrentBlockchainStatus::get_tx(tx_hash, tx)) + { + // if tx not found in the blockchain, check if its in mempool + + vector> mempool_txs = + CurrentBlockchainStatus::get_mempool_txs(); + + + for (auto const& mtx: mempool_txs) + { + + cout << (get_transaction_hash(mtx.second)) << endl; + if (get_transaction_hash(mtx.second) == tx_hash) + { + tx = mtx.second; + tx_found = true; + break; + } + } + + tx_found = false; + } + else + { + tx_found = true; + } + + if (tx_found) + { + crypto::hash tx_hash = get_transaction_hash(tx); + + // return tx hash. can be used to check if we acctually + // delivered the tx that was requested + j_response["tx_hash"] = pod_to_hex(tx_hash); + + j_response["pub_key"] = pod_to_hex(xmreg::get_tx_pub_key_from_received_outs(tx)); + + + bool coinbase = is_coinbase(tx); + + j_response["coinbase"] = coinbase; + + // key images of inputs + vector input_key_imgs; + + // public keys and xmr amount of outputs + vector> output_pub_keys; + + uint64_t xmr_inputs; + uint64_t xmr_outputs; + uint64_t num_nonrct_inputs; + uint64_t fee; + uint64_t mixin_no; + uint64_t size; + uint64_t blk_height; + + // sum xmr in inputs and ouputs in the given tx + array const& sum_data = xmreg::summary_of_in_out_rct( + tx, output_pub_keys, input_key_imgs); + + xmr_outputs = sum_data[0]; + xmr_inputs = sum_data[1]; + mixin_no = sum_data[2]; + num_nonrct_inputs = sum_data[3]; + + j_response["xmr_outputs"] = xmr_outputs; + j_response["xmr_inputs"] = xmr_inputs; + j_response["mixin_no"] = mixin_no; + + fee = 0; + + if (!coinbase && tx.vin.size() > 0) + { + // check if not miner tx + // i.e., for blocks without any user transactions + if (tx.vin.at(0).type() != typeid(txin_gen)) + { + // get tx fee + fee = get_tx_fee(tx); + } + } + + j_response["fee"] = fee; + + // get tx size in bytes + size = get_object_blobsize(tx); + + j_response["size"] = size; + + int64_t tx_height {-1}; + + int64_t no_confirmations {-1}; + + if (CurrentBlockchainStatus::get_tx_block_height(tx_hash, tx_height)) + { + // get the current blockchain height. Just to check + uint64_t bc_height = get_current_blockchain_height(); + + no_confirmations = bc_height - tx_height; + } + + j_response["tx_height"] = tx_height; + j_response["no_confirmations"] = no_confirmations; + j_response["status"] = "OK"; + j_response["error"] = ""; + } + else + { + cerr << "Cant get tx details for tx hash! : " << tx_hash_str << '\n'; + j_response["status"] = "Cant get tx details for tx hash! : " + tx_hash_str; + } + + string response_body = j_response.dump(); + + auto response_headers = make_headers({{ "Content-Length", to_string(response_body.size())}}); + + session->close( OK, response_body, response_headers); +} + + void YourMoneroRequests::get_version(const shared_ptr< Session > session, const Bytes & body) { diff --git a/src/YourMoneroRequests.h b/src/YourMoneroRequests.h index dd35ff8..70dda20 100755 --- a/src/YourMoneroRequests.h +++ b/src/YourMoneroRequests.h @@ -105,6 +105,9 @@ public: void import_recent_wallet_request(const shared_ptr< Session > session, const Bytes & body); + void + get_tx(const shared_ptr< Session > session, const Bytes & body); + void get_version(const shared_ptr< Session > session, const Bytes & body); diff --git a/src/tools.cpp b/src/tools.cpp index 5e5417f..7995c80 100755 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -290,7 +290,112 @@ get_blockchain_path(bf::path& blockchain_path, } -uint64_t + + +array +summary_of_in_out_rct( + const transaction& tx, + vector>& output_pub_keys, + vector& input_key_imgs) +{ + + uint64_t xmr_outputs {0}; + uint64_t xmr_inputs {0}; + uint64_t mixin_no {0}; + uint64_t num_nonrct_inputs {0}; + + + for (const tx_out& txout: tx.vout) + { + if (txout.target.type() != typeid(txout_to_key)) + { + // push empty pair. + output_pub_keys.push_back(pair{}); + continue; + } + + // get tx input key + const txout_to_key& txout_key + = boost::get(txout.target); + + output_pub_keys.push_back(make_pair(txout_key, txout.amount)); + + xmr_outputs += txout.amount; + } + + size_t input_no = tx.vin.size(); + + for (size_t i = 0; i < input_no; ++i) + { + + if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) + { + continue; + } + + // get tx input key + const cryptonote::txin_to_key& tx_in_to_key + = boost::get(tx.vin[i]); + + xmr_inputs += tx_in_to_key.amount; + + if (tx_in_to_key.amount != 0) + { + ++num_nonrct_inputs; + } + + if (mixin_no == 0) + { + mixin_no = tx_in_to_key.key_offsets.size(); + } + + input_key_imgs.push_back(tx_in_to_key); + + } // for (size_t i = 0; i < input_no; ++i) + + + return {xmr_outputs, xmr_inputs, mixin_no, num_nonrct_inputs}; +}; + + +// this version for mempool txs from json +array +summary_of_in_out_rct(const json& _json) +{ + uint64_t xmr_outputs {0}; + uint64_t xmr_inputs {0}; + uint64_t no_outputs {0}; + uint64_t no_inputs {0}; + uint64_t mixin_no {0}; + uint64_t num_nonrct_inputs {0}; + + for (const json& vout: _json["vout"]) + { + xmr_outputs += vout["amount"].get(); + } + + no_outputs = _json["vout"].size(); + + for (const json& vin: _json["vin"]) + { + uint64_t amount = vin["key"]["amount"].get(); + + xmr_inputs += amount; + + if (amount != 0) + ++num_nonrct_inputs; + } + + no_inputs = _json["vin"].size(); + + mixin_no = _json["vin"].at(0)["key"]["key_offsets"].size() - 1; + + return {xmr_outputs, xmr_inputs, no_outputs, no_inputs, mixin_no, num_nonrct_inputs}; +}; + + + + uint64_t sum_money_in_outputs(const transaction& tx) { uint64_t sum_xmr {0}; diff --git a/src/tools.h b/src/tools.h index 99fd1b0..4cf46d1 100755 --- a/src/tools.h +++ b/src/tools.h @@ -105,6 +105,16 @@ bool get_blockchain_path(bf::path& blockchain_path, bool testnet = false); +array +summary_of_in_out_rct( + const transaction& tx, + vector>& output_pub_keys, + vector& input_key_imgs); + +// this version for mempool txs from json +array +summary_of_in_out_rct(const json& _json); + uint64_t sum_money_in_outputs(const transaction& tx);