Merge pull request #39 from moneroexamples/new_tx_details

New tx details modal window
pull/30/head
moneroexamples 7 years ago committed by GitHub
commit e63d9b99d8

@ -102,7 +102,7 @@ Download and compile recent Monero realease into your home folder:
# first install monero dependecines
sudo apt update
sudo apt install git build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libcurl4-openssl-dev libgtest-dev
sudo apt install git build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libcurl4-openssl-dev libgtest-dev libreadline-dev
# go to home folder
cd ~
@ -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

File diff suppressed because one or more lines are too long

@ -65,6 +65,7 @@
<script src="js/controllers/transactions.js?1"></script>
<script src="js/controllers/vanity_address.js?1"></script>
<script src="js/controllers/account.js?1"></script>
<script src="js/controllers/transaction_details.js"></script>
<!--<script src="js/directives/scroll.js?1"></script>-->
<script src="js/directives/qr_code.js?1"></script>
<script src="js/directives/modal.js?1"></script>

@ -1,5 +1,7 @@
var config = {
apiUrl: "http://127.0.0.1:1984/",
mainnetExplorerUrl: "https://xmrchain.net/",
testnetExplorerUrl: "http://139.162.32.245:8082/",
testnet: true,
coinUnitPlaces: 12,
txMinConfirms: 10, // corresponds to CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE in Monero

@ -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);

@ -43,6 +43,9 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q,
$scope.sent_tx = {};
var view_only = AccountService.isViewOnly();
var explorerUrl = config.testnet ? config.testnetExplorerUrl : config.mainnetExplorerUrl;
// few multiplayers based on uint64_t wallet2::get_fee_multiplier
var fee_multiplayers = [1, 4, 20, 166];
@ -453,7 +456,8 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q,
payment_id: payment_id,
tx_id: tx_hash,
tx_prvkey: tx_prvkey,
tx_fee: neededFee/*.add(getTxCharge(neededFee))*/
tx_fee: neededFee/*.add(getTxCharge(neededFee))*/,
explorerLink: explorerUrl + "tx/" + tx_hash
};
$scope.success_page = true;

@ -0,0 +1,115 @@
/**
* Created by mwo on 02/08/17.
*/
thinwalletCtrls.controller('TransactionDetailsCtrl', function ($scope,
AccountService,
ModalService,
ApiCalls) {
"use strict";
// we assume that we have tx_hash in the modal's url,
// e.g. transaction-details?tx_hash=963773fce9f1e8f285f59cdc3cc69883f43de1c4edaa7b01cc5522b48cad9631
// so we need to extract the hash, validate it, and use to make an ajax query to the backend
// for more detailed info about this tx.
$scope.error = "";
var tx_hash = ModalService.getModalUrlParams("tx_hash");
//console.log(tx_hash);
if (tx_hash === null) {
$scope.tx_hash = "tx_hash is not provided";
return;
}
var view_only = AccountService.isViewOnly();
$scope.tx_hash = tx_hash;
// check the tx_hash if it has expected format
if (tx_hash.length !== 64 || !(/^[0-9a-fA-F]{64}$/.test(tx_hash)))
{
$scope.error = "Tx hash has incorrect format";
return;
}
var explorerUrl = config.testnet ? config.testnetExplorerUrl : config.mainnetExplorerUrl;
var address = AccountService.getAddress();
var view_key = AccountService.getViewKey();
// tx_hash seems ok, so ask the backed for its details.
ApiCalls.get_tx(address, view_key, tx_hash)
.then(function(response) {
var data = response.data;
if (data.error) {
$scope.error = data.status;
return;
}
// set data to be shown in the modal window
$scope.ring_size = data.mixin_no;
$scope.fee = data.fee;
$scope.tx_size = (data.size / 1024).toFixed(4);
$scope.no_confirmations = data.no_confirmations === -1 ? "tx in mempool" : data.no_confirmations;
$scope.tx_height = data.tx_height === -1 ? "N.A" :data.tx_height;
$scope.tx_pub_key = data.pub_key;
$scope.coinbase = data.coinbase;
$scope.timestamp = new Date(data.timestamp * 1000);
var age_duration = moment.duration(new Date() - new Date(data.timestamp * 1000));
$scope.tx_age = age_duration.humanize();
var total_received = data.total_received;
var total_sent = data.total_sent;
// filterout wrongly guessed key images by the frontend
if ((data.spent_outputs || []).length > 0) {
if (view_only === false) {
for (var j = 0; j < data.spent_outputs.length; ++j) {
var key_image = AccountService.cachedKeyImage(
data.spent_outputs[j].tx_pub_key,
data.spent_outputs[j].out_index
);
if (data.spent_outputs[j].key_image !== key_image) {
total_sent = new JSBigInt(total_sent).subtract(data.spent_outputs[j].amount);
data.spent_outputs.splice(j, 1);
j--;
}
}
}
}
$scope.tx_amount = new JSBigInt(total_received || 0).subtract(total_sent || 0).toString();
// decrypt payment_id8 which results in using
// integrated address
if (data.payment_id.length == 16) {
if (data.pub_key) {
var decrypted_payment_id8
= decrypt_payment_id(data.payment_id,
data.pub_key,
AccountService.getViewKey());
//console.log("decrypted_payment_id8: " + decrypted_payment_id8);
data.payment_id = decrypted_payment_id8;
}
}
$scope.payment_id = data.payment_id;
//console.log($scope.tx_amount);
}, function(data) {
$scope.error = 'Failed to get tx detailed from the backend';
});
$scope.explorerLink = explorerUrl + "tx/" + tx_hash;
});

@ -32,6 +32,7 @@
<div class="transaction-address">
<span class="bold">Block no:</span> &nbsp;&nbsp;{{tx_is_mempool(tx) ? 'N/A (tx in mempool)' : tx.height}}
&nbsp;&nbsp;&nbsp;&nbsp;<span class="bold">Payment ID:</span> &nbsp;&nbsp;{{tx.payment_id || "N/A"}}
<a style="color: #5093eb" show-modal="transaction-details?tx_hash={{ tx.hash }}">more details</a>
</div>
</div>
</div>

@ -75,5 +75,13 @@ thinwalletServices
})
};
api.get_tx = function(public_address, view_key, tx_hash) {
return $http.post(config.apiUrl + 'get_tx', {
address: public_address,
view_key: view_key,
tx_hash: tx_hash
})
};
return api;
});

@ -33,13 +33,14 @@ thinwalletServices
var currentModal = '';
modalService.show = function (modal_name) {
console.log("Showing modal: " + modal_name);
//console.log("Showing modal: " + modal_name);
currentModal = modal_name;
};
modalService.hide = function (modal_name) {
console.log("Hiding modal: " + modal_name);
if (!modal_name || currentModal === modal_name) {
//console.log("Hiding modal: " + modal_name + ", " + currentModal);
if (!modal_name || currentModal.split("?")[0] === modal_name) {
currentModal = '';
}
};
@ -50,8 +51,23 @@ thinwalletServices
modalService.getModalURL = function () {
if (!currentModal) return '';
return "modals/" + currentModal + ".html?1";
var split_by_param = currentModal.split("?");
// we may pass parameters in model window name, e.g.,
// transaction-details?tx_hash=963773fce9f1e8f285f59cdc3cc69883f43de1c4edaa7b01cc5522b48cad9631
var modal_url = "modals/" + split_by_param[0] + ".html?1";
return modal_url;
};
modalService.getModalUrlParams = function(param_to_get) {
// the "http://127.0.0.1/" is dummy below, only used
// so that URL does not complain
var url_parsed = new URL("http://127.0.0.1/" + currentModal);
return url_parsed.searchParams.get(param_to_get);
};
return modalService;
});

@ -0,0 +1,65 @@
<div class="review-details-modal" modal-name="transaction-details" ng-controller="TransactionDetailsCtrl">
<div class="w-container review-details-container">
<div class="w-clearfix review-details-div">
<a class="w-inline-block close-modal" data-ix="close-review-details" hide-modal>
<div class="close-overlay pointer" data-ix="close-overlay">+</div>
</a>
<h1 class="head-modal">Transaction details</h1>
<div class="subhead-text modal review" style="margin-bottom: 10px">{{ tx_hash }}</div>
<div class="w-form form-wrapper">
<div class="w-row">
<div class="w-col w-col-3 responsive-column">
<label class="field-label review" for="Amount">Amount</label>
<div class="review-text">{{ tx_amount | money}}</div>
</div>
<div class="w-col w-col-3 responsive-column">
<label class="field-label review">Ring Size</label>
<div class="review-text">{{ ring_size }}</div>
</div>
<div class="w-col w-col-3 responsive-column">
<label class="field-label review">Block no</label>
<div class="review-text">{{ tx_height }}</div>
</div>
<div class="w-col w-col-3 responsive-column">
<label class="field-label review">Size [kB]</label>
<div class="review-text">{{ tx_size }}</div>
</div>
</div>
<div class="w-row">
<div class="w-col w-col-3 responsive-column">
<label class="field-label review" for="Amount">No of confirmations</label>
<div class="review-text">{{no_confirmations}}</div>
</div>
<div class="w-col w-col-3 responsive-column">
<label class="field-label review">Fee</label>
<div class="review-text">{{fee | money}}</div>
</div>
<div class="w-col w-col-6 responsive-column">
<label class="field-label review">Timestamp (age)</label>
<div class="review-text">{{timestamp | time}} ({{ tx_age }})</div>
</div>
</div>
<div class="w-row">
<div class="w-col w-col-2 responsive-column">
<label class="field-label review">Is coinbase?</label>
<div class="review-text">{{coinbase}}</div>
</div>
<div class="w-col w-col-10 responsive-column">
<label class="field-label review" for="Mnemonic-2">Payment ID</label>
<div class="review-text">{{payment_id || "N/A"}}</div>
</div>
</div>
<div class="w-row">
<div class="w-col w-col-12 responsive-column">
<label class="field-label review" for="Mnemonic-2">View in a blockchain explorer for even more details</label>
<a class="login-link" href="{{ explorerLink }} " target="_blank">{{ explorerLink }} </a>
</div>
</div>
<br/>
<div class="w-form-fail error-backing" style="display: block;" ng-show="!!error">
<p>{{error}}</p>
</div>
</div>
</div>
</div>
</div>

@ -104,6 +104,10 @@
<div class="move-text-div">
<div class="middle-text receive">{{sent_tx.tx_prvkey}}</div>
</div>
<label class="send-label" >Soon should be visible in a blockchain explorer</label>
<div class="move-text-div">
<a class="login-link" href="{{ sent_tx.explorerLink }} " target="_blank">{{ sent_tx.explorerLink }}</a>
</div>
<input class="w-button send-btn" type="submit" value="Send Another Payment" data-wait="Please wait...">
</form>
</div>

@ -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<Settings>( );

@ -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
@ -813,6 +813,27 @@ CurrentBlockchainStatus::get_searched_blk_no(const string& address,
return true;
}
bool
CurrentBlockchainStatus::get_known_outputs_keys(
string const& address,
vector<pair<string, uint64_t>>& known_outputs_keys)
{
std::lock_guard<std::mutex> lck (searching_threads_map_mtx);
if (!search_thread_exist(address))
{
// thread does not exist
cout << "thread for " << address << " does not exist" << endl;
return false;
}
known_outputs_keys
= searching_threads[address].get()->get_known_outputs_keys();
return true;
}
bool
CurrentBlockchainStatus::search_thread_exist(const string& address)
{
@ -863,8 +884,46 @@ CurrentBlockchainStatus::find_txs_in_mempool(
return true;
};
bool
CurrentBlockchainStatus::find_tx_in_mempool(
crypto::hash const& tx_hash,
transaction& tx)
{
}
bool
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;
}
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<std::mutex> lck (searching_threads_map_mtx);

@ -188,6 +188,19 @@ struct CurrentBlockchainStatus
find_txs_in_mempool(const string& address_str,
json& transactions);
static bool
find_tx_in_mempool(crypto::hash const& tx_hash,
transaction& tx);
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);
@ -196,6 +209,10 @@ struct CurrentBlockchainStatus
get_searched_blk_no(const string& address,
uint64_t& searched_blk_no);
static bool
get_known_outputs_keys(string const& address,
vector<pair<string, uint64_t>>& known_outputs_keys);
static void
clean_search_thread_map();

@ -304,8 +304,7 @@ TxSearch::search()
known_outputs_keys.push_back(make_pair(out_info.pub_key, out_info.amount));
}
} // for (auto &out_k_idx: found_mine_outputs)
} // for (auto& out_info: oi_identification.identified_outputs)
} // if (!found_mine_outputs.empty())
@ -526,6 +525,12 @@ TxSearch::populate_known_outputs()
}
}
vector<pair<string, uint64_t>>
TxSearch::get_known_outputs_keys()
{
std::lock_guard<std::mutex> lck (getting_known_outputs_keys);
return known_outputs_keys;
};
json
TxSearch::find_txs_in_mempool(
@ -535,16 +540,7 @@ TxSearch::find_txs_in_mempool(
uint64_t current_height = CurrentBlockchainStatus::get_current_blockchain_height();
vector<pair<string, uint64_t>> known_outputs_keys_copy;
{
// copy known ouputs. mutex is needed as known_outputs_keys can
// be updated at the same time as used in this here.
// we make its copy as to keep mutex for short time,
// and read only on copy safely.
std::lock_guard<std::mutex> lck (getting_known_outputs_keys);
known_outputs_keys_copy = known_outputs_keys;
}
vector<pair<string, uint64_t>> known_outputs_keys_copy = get_known_outputs_keys();
// since find_txs_in_mempool can be called outside of this thread,
// we need to use local connection. we cant use connection that the

@ -94,6 +94,9 @@ public:
void
populate_known_outputs();
vector<pair<string, uint64_t>>
get_known_outputs_keys();
/**
* Search for our txs in the mempool

@ -146,7 +146,7 @@ YourMoneroRequests::get_address_txs(const shared_ptr< Session > session, const B
json j_response;
json j_request;
vector<string> requested_values{"address", "view_key"};
vector<string> requested_values {"address", "view_key"};
if (!parse_request(body, requested_values, j_request, j_response))
{
@ -1006,6 +1006,342 @@ YourMoneroRequests::import_recent_wallet_request(const shared_ptr< Session > ses
}
void
YourMoneroRequests::get_tx(const shared_ptr< Session > session, const Bytes & body)
{
json j_response;
json j_request;
vector<string> requested_values {"address" , "view_key", "tx_hash"};
if (!parse_request(body, requested_values, j_request, j_response))
{
session_close(session, j_response.dump());
return;
}
string const& xmr_address = j_request["address"];
string const& view_key = j_request["view_key"];
string const& tx_hash_str = j_request["tx_hash"];
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;
uint64_t default_timestamp {0};
bool tx_found {false};
bool tx_in_mempool {false};
if (!CurrentBlockchainStatus::get_tx(tx_hash, tx))
{
// if tx not found in the blockchain, check if its in mempool
vector<pair<uint64_t, transaction>> mempool_txs =
CurrentBlockchainStatus::get_mempool_txs();
//cout << "serach mempool" << endl;
for (auto const& mtx: mempool_txs)
{
//cout << (get_transaction_hash(mtx.second)) << '\n';
if (get_transaction_hash(mtx.second) == tx_hash)
{
tx = mtx.second;
tx_found = true;
tx_in_mempool = true;
default_timestamp = mtx.first;
break;
}
}
}
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<txin_to_key> input_key_imgs;
// public keys and xmr amount of outputs
vector<pair<txout_to_key, uint64_t>> 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<uint64_t, 4> 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;
// to be field later on using data from OutputInputIdentification
j_response["total_sent"] = 0;
j_response["total_received"] = 0;
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;
}
// Class that is responsible for identification of our outputs
// and inputs in a given tx.
j_response["payment_id"] = string {};
j_response["timestamp"] = default_timestamp;
account_public_address address;
secret_key viewkey;
// 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
// to double check tx data in the frontend, and also maybe try doing
// it differently than before. Its not great, since we reinvent the wheel
// but its worth double checking the mysql data, and also allows for new
// implementation in the frontend.
if (CurrentBlockchainStatus::get_xmr_address_viewkey(xmr_address, address, viewkey))
{
OutputInputIdentification oi_identification {&address, &viewkey, &tx};
oi_identification.identify_outputs();
uint64_t total_received {0};
// 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;
}
j_response["total_received"] = total_received;
json j_spent_outputs = json::array();
// to get spendings, we need to have our key_images. but
// the backend does not have spendkey, so it cant determine
// which key images are really ours or not. this is the task
// for the frontend. however, backend can only provide guesses and
// nessessery data to the frontend to filter out incorrect
// guesses.
//
// for input identification, we will use our mysql. its just much
// faster to use it here, than before. but first we need to
// get account id of the user asking for tx details.
// a placeholder for exciting or new account data
XmrAccount acc;
// select this account if its existing one
if (xmr_accounts->select(xmr_address, acc))
{
// if user exist, get tx data from database
// this will work only for tx in the blockchain,
// not those in the mempool.
if (!tx_in_mempool)
{
// if not in mempool, but in blockchain, just
// get data aout key images from the mysql
XmrTransaction xmr_tx;
if (xmr_accounts->tx_exists(acc.id, tx_hash_str, xmr_tx))
{
j_response["payment_id"] = xmr_tx.payment_id;
j_response["timestamp"] = static_cast<uint64_t>(xmr_tx.timestamp);
vector<XmrInput> inputs;
if (xmr_accounts->select_inputs_for_tx(xmr_tx.id, inputs))
{
json j_spent_outputs = json::array();
uint64_t total_spent {0};
for (XmrInput input: inputs)
{
XmrOutput out;
if (xmr_accounts->select_output_with_id(input.output_id, out))
{
total_spent += input.amount;
j_spent_outputs.push_back({
{"amount" , input.amount},
{"key_image" , input.key_image},
{"tx_pub_key" , out.tx_pub_key},
{"out_index" , out.out_index},
{"mixin" , out.mixin}});
}
} // for (XmrInput input: inputs)
j_response["total_sent"] = total_spent;
j_response["spent_outputs"] = j_spent_outputs;
} // if (xmr_accounts->select_inputs_for_tx(tx.id, inputs))
} // if (xmr_accounts->tx_exists(acc.id, tx_hash_str, xmr_tx))
} // if (!tx_in_mempool)
else
{
// if tx in mempool, mysql will not have this tx, so
// we cant pull info about key images from mysql for this tx.
// we have to redo this info from basically from scrach.
vector<pair<string, uint64_t>> known_outputs_keys;
if (CurrentBlockchainStatus::get_known_outputs_keys(
xmr_address, known_outputs_keys))
{
// we got known_outputs_keys from the search thread.
// so now we can use OutputInputIdentification to
// get info about inputs.
// Class that is resposnible for idenficitaction of our outputs
// and inputs in a given tx.
OutputInputIdentification oi_identification
{&address, &viewkey, &tx};
// 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);
json j_spent_outputs = json::array();
uint64_t total_spent {0};
for (auto& in_info: oi_identification.identified_inputs)
{
// need to get output info from mysql, as we need
// to know output's amount, its orginal
// tx public key and its index in that tx
XmrOutput out;
if (xmr_accounts->output_exists(in_info.out_pub_key, out))
{
total_spent += out.amount;
j_spent_outputs.push_back({
{"amount" , in_info.amount},
{"key_image" , in_info.key_img},
{"tx_pub_key" , out.tx_pub_key},
{"out_index" , out.out_index},
{"mixin" , out.mixin}});
}
} // for (auto& in_info: oi_identification.identified_inputs)
j_response["total_sent"] = total_spent;
j_response["spent_outputs"] = j_spent_outputs;
} //if (CurrentBlockchainStatus::get_known_outputs_keys(
// xmr_address, known_outputs_keys))
} // else
} // if (xmr_accounts->select(xmr_address, acc))
} // if (CurrentBlockchainStatus::get_xmr_address_viewkey(address_str, address, viewkey)
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)
{

@ -31,7 +31,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define OPENMONERO_RPC_VERSION_MAJOR 1
#define OPENMONERO_RPC_VERSION_MINOR 0
#define OPENMONERO_RPC_VERSION_MINOR 1
#define MAKE_OPENMONERO_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define OPENMONERO_RPC_VERSION \
MAKE_OPENMONERO_RPC_VERSION(OPENMONERO_RPC_VERSION_MAJOR, OPENMONERO_RPC_VERSION_MINOR)
@ -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);

@ -290,7 +290,112 @@ get_blockchain_path(bf::path& blockchain_path,
}
uint64_t
array<uint64_t, 4>
summary_of_in_out_rct(
const transaction& tx,
vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
vector<txin_to_key>& 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<txout_to_key, uint64_t>{});
continue;
}
// get tx input key
const txout_to_key& txout_key
= boost::get<cryptonote::txout_to_key>(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<cryptonote::txin_to_key>(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<uint64_t, 6>
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<uint64_t>();
}
no_outputs = _json["vout"].size();
for (const json& vin: _json["vin"])
{
uint64_t amount = vin["key"]["amount"].get<uint64_t>();
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};

@ -105,6 +105,16 @@ bool
get_blockchain_path(bf::path& blockchain_path,
bool testnet = false);
array<uint64_t, 4>
summary_of_in_out_rct(
const transaction& tx,
vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
vector<txin_to_key>& input_key_imgs);
// this version for mempool txs from json
array<uint64_t, 6>
summary_of_in_out_rct(const json& _json);
uint64_t
sum_money_in_outputs(const transaction& tx);

Loading…
Cancel
Save