Merge pull request #2 from moneroexamples/deal_with_orphaned_blocks

Deal with orphaned blocks
pull/3/head
moneroexamples 7 years ago committed by GitHub
commit 65fa53700d

@ -30,6 +30,7 @@ set(MONERO_HEADERS_DIR
include_directories(
${MONERO_HEADERS_DIR}/src
${MONERO_HEADERS_DIR}/external
${MONERO_HEADERS_DIR}/external/easylogging++
${MONERO_HEADERS_DIR}/contrib/epee/include
${MONERO_HEADERS_DIR}/external/db_drivers/liblmdb)
@ -60,6 +61,10 @@ add_library(mnemonics STATIC IMPORTED)
set_property(TARGET mnemonics
PROPERTY IMPORTED_LOCATION ${MONERO_LIBS_DIR}/libmnemonics.a)
add_library(epee STATIC IMPORTED)
set_property(TARGET epee
PROPERTY IMPORTED_LOCATION ${MONERO_LIBS_DIR}/libepee.a)
add_library(blockchain_db STATIC IMPORTED)
set_property(TARGET blockchain_db
PROPERTY IMPORTED_LOCATION ${MONERO_LIBS_DIR}/libblockchain_db.a)
@ -110,6 +115,7 @@ target_link_libraries(restbed_xmr
ringct
common
mnemonics
epee
mysqlpp
mysqlclient
${Boost_LIBRARIES}

@ -1,17 +1,27 @@
# JSON REST service for Monero
Example of using [restbed](https://github.com/Corvusoft/restbed/) to provide Monero related JSON REST service.
Example of using [restbed](https://github.com/Corvusoft/restbed/) to
provide Monero related JSON REST service.
For the example, a service called Open Monero was developed.
## Open Monero
Open Monero is an open source implementation of backend for
https://mymonero.com/. The frontend, html, css, JavaScript, were adapted
from, and originally developed by https://mymonero.com/.
However, unlike MyMonero's backend, Open Monero's backend is open sourced, free
to use, host and modify.
Open Monero is an open source implementation of backend of
https://mymonero.com/. The frontend, which includes html, css, JavaScript were adapted
from (and originally developed by) https://mymonero.com/. The backend in this example
was developed to be fully compatible with the frontend.
However, unlike MyMonero, Open Monero's backend is open sourced, free
to use, host and modify. Additionally, the following features were added/changed:
- google analytics, cloudflare, images and flash were removed.
- transaction fees were set to zero.
- the wallets generated use 25 word mnemonics, fully compatible with official monero wallets
(13 word mnemonics generated by MyMonero work as usual though).
- import wallet fee was reduced.
- backend correctly decodes RingCT transactions.
- support of testnet network added.
## Status
Still under development as its not finished.

@ -1,2 +0,0 @@
/*! ngClip 17-09-2014 */
!function(a,b){"use strict";b.module("ngClipboard",[]).provider("ngClip",function(){var a=this;return this.path="//cdnjs.cloudflare.com/ajax/libs/zeroclipboard/2.1.6/ZeroClipboard.swf",{setPath:function(b){a.path=b},setConfig:function(b){a.config=b},$get:function(){return{path:a.path,config:a.config}}}}).run(["ngClip",function(a){var c={swfPath:a.path,trustedDomains:["*"],allowScriptAccess:"always",forceHandCursor:!0};ZeroClipboard.config(b.extend(c,a.config||{}))}]).directive("clipCopy",["ngClip",function(){return{scope:{clipCopy:"&",clipClick:"&",clipClickFallback:"&"},restrict:"A",link:function(a,c,d){if(ZeroClipboard.isFlashUnusable())return void c.bind("click",function(b){a.$apply(a.clipClickFallback({$event:b,copy:a.$eval(a.clipCopy)}))});var e=new ZeroClipboard(c);""===d.clipCopy&&(a.clipCopy=function(){return c[0].previousElementSibling.innerText}),e.on("ready",function(){e.on("copy",function(b){var c=b.clipboardData;c.setData("text/plain",a.$eval(a.clipCopy))}),e.on("aftercopy",function(){b.isDefined(d.clipClick)&&a.$apply(a.clipClick)}),a.$on("$destroy",function(){e.destroy()})})}}}])}(window,window.angular);

File diff suppressed because one or more lines are too long

@ -36,8 +36,6 @@
<script src="bower_components/angular-route/angular-route.min.js"></script>
<script src="bower_components/ngInfiniteScroll/build/ng-infinite-scroll.min.js"></script>
<script src="bower_components/ng-idle/angular-idle.min.js"></script>
<script src="bower_components/zeroclipboard/dist/ZeroClipboard.min.js"></script>
<script src="bower_components/ng-clip/dest/ng-clip.min.js"></script>
<script src="bower_components/moment/min/moment-with-locales.min.js"></script>
<script src="js/webflow.js"></script>
@ -68,7 +66,6 @@
<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/directives/clipboard.js?1"></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>

@ -45,7 +45,6 @@ var thinwalletDirectives = angular.module('thinWallet.Directives', [
var thinwalletApp = angular.module('thinWallet', [
'ngRoute',
'ngClipboard',
'thinWallet.Controllers',
'thinWallet.Services',
'thinWallet.Filters',
@ -58,13 +57,12 @@ thinwalletApp.config(['$httpProvider', function($httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
thinwalletApp.config(function ($idleProvider, $keepaliveProvider, ngClipProvider) {
thinwalletApp.config(function ($idleProvider, $keepaliveProvider) {
"use strict";
$idleProvider.idleDuration(config.idleTimeout * 60);
$idleProvider.warningDuration(config.idleWarningDuration);
$keepaliveProvider.interval(10);
ngClipProvider.setPath("../bower_components/zeroclipboard/dist/ZeroClipboard.swf");
});
thinwalletApp.run(function ($rootScope, $route, $location, $http, $timeout, $idle, EVENT_CODES, AccountService, ModalService) {

@ -1,6 +1,6 @@
var config = {
apiUrl: "http://127.0.0.1:1984/",
testnet: false,
testnet: true,
coinUnitPlaces: 12,
txMinConfirms: 10,
coinSymbol: 'XMR',

@ -1,53 +0,0 @@
// Copyright (c) 2014-2015, MyMonero.com
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// 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.
thinwalletDirectives.directive('copyToClipboard', function($timeout, $interval) {
return {
scope: {
copyToClipboard: '='
},
template: '<i class="fa fa-clipboard" clip-copy="copyToClipboard" clip-click="clicked()" clip-click-fallback="fallback(copy)"></i>',
link: function(scope, element) {
scope.fallback = function(copy) {
window.prompt("Press Ctrl+C to copy the text below.", copy);
};
scope.clicked = function() {
var icon = element.find('i');
icon.removeClass('fa-clipboard').addClass('fa-check-circle');
$timeout(function() {
var interval = $interval(function() {
if (!icon.hasClass('zeroclipboard-is-hover')) {
icon.removeClass('fa-check-circle').addClass('fa-clipboard');
$interval.cancel(interval);
}
}, 50);
}, 1000);
};
}
};
});

@ -15,16 +15,16 @@
easy for you to copy and paste it into simplewallet.</div>
</div>
<label class="field-label review" for="Mnemonic-2">Payment command</label>
<div class="review-text address">{{command}} <span copy-to-clipboard="command"></span></div>
<div class="review-text address">{{command}} </div>
</div>
<label class="field-label review" for="Mnemonic-2">Payment address</label>
<div class="move-text-div">
<div class="review-text">import.mymonero.com (or use the full address below)</div>
<div class="review-text address">({{payment_address}} <span copy-to-clipboard="payment_address"></span>)</div>
<div class="review-text address">({{payment_address}} )</div>
</div>
<label class="field-label review" for="Mnemonic-2">Payment ID (make sure to use this)</label>
<div class="move-text-div">
<div class="review-text">{{payment_id}} <span copy-to-clipboard="payment_id"></span></div>
<div class="review-text">{{payment_id}}</div>
</div>
<label class="field-label review" for="Mnemonic-2">Payment status</label>
<div class="move-text-div">

@ -22,7 +22,7 @@
</div>
<label class="field-label review" for="Mnemonic-2">Address</label>
<div class="move-text-div">
<div class="review-text address">{{openaliasDialog.address}} <span copy-to-clipboard="openaliasDialog.address"></span></div>
<div class="review-text address">{{openaliasDialog.address}} </span></div>
</div>
<div class="move-text-div" style="margin-top: 10px;">
<div class="review-text bold">{{openaliasDialog.dnssec_used ? 'This address passed an additional check, DNSSEC, and is most probably correct' : 'WARNING: this address could not be validated via an additional check, DNSSEC, and thus may not be correct'}}</div>

@ -10,15 +10,15 @@
<form id="email-form" name="email-form" data-name="Email Form">
<label class="field-label review" for="Mnemonic-2">Account Address (Public)</label>
<div class="move-text-div">
<div class="review-text address">{{address}} <span copy-to-clipboard="address"></span></div>
<div class="review-text address">{{address}} </div>
</div>
<label class="field-label review" for="Mnemonic-2">View Key (Private)</label>
<div class="move-text-div">
<div class="review-text">{{view_key}} <span copy-to-clipboard="view_key"></span></div>
<div class="review-text">{{view_key}} </div>
</div>
<label class="field-label review" for="Mnemonic-2">Spend Key (Private)</label>
<div class="move-text-div">
<div class="review-text">{{spend_key}} <span copy-to-clipboard="spend_key"></span></div>
<div class="review-text">{{spend_key}} </div>
</div>
<div class="submit-div">
<a class="login-btn modals pointer" data-ix="close-review-details" hide-modal>Ok, thanks!</a>

@ -9,7 +9,7 @@
<div class="w-form form-wrapper">
<form id="email-form" name="email-form" data-name="Email Form">
<label class="field-label" for="Mnemonic-2">Your Private Login Key</label>
<div class="review-text">{{mnemonic}} <span copy-to-clipboard="mnemonic"></span></div>
<div class="review-text">{{mnemonic}} </div>
<div class="submit-div">
<a class="login-btn modals pointer" data-ix="close-login-key" hide-modal>Ok, thanks!</a>
</div>

@ -45,7 +45,7 @@
<div class="middle-div">
<div class="overview-body-head">Address</div>
<div class="move-text-div">
<p class="middle-text">{{address}} <span copy-to-clipboard="address"></span></p>
<p class="middle-text">{{address}}</p>
</div>
</div>
<div>

@ -27,7 +27,7 @@
<form id="email-form" name="email-form" data-name="Email Form" form-autofill-fix>
<label class="send-label" for="Receiver-address">Address</label>
<div class="move-text-div">
<div class="middle-text receive">{{address}} <span copy-to-clipboard="address"></span></div>
<div class="middle-text receive">{{address}} </div>
</div>
<div class="w-row">
<div class="w-col w-col-6 responsive-column">
@ -67,7 +67,7 @@
<div class="monero-code"><span class="help-text">Use this QR code to quickly receive payments</span>
</div>
<div class="move-text-div">
<a ng-href="{{params | paymentUri}}" class="qr-code-text">{{params | paymentUri}}</a> <span copy-to-clipboard="params | paymentUri"></span>
<a ng-href="{{params | paymentUri}}" class="qr-code-text">{{params | paymentUri}}</a> </span>
</div>
<div class="info-div">
<div class="overview-body-head">Why do I&nbsp;need a QR&nbsp;code?</div>

@ -11,12 +11,6 @@ using namespace restbed;
using boost::filesystem::path;
// needed for log system of momero
namespace epee {
unsigned int g_test_dbg_lock_sleep = 0;
}
int
main(int ac, const char* av[])
{

@ -3,9 +3,9 @@
-- https://www.phpmyadmin.net/
--
-- Host: localhost
-- Generation Time: Jan 13, 2017 at 04:51 AM
-- Generation Time: Jan 23, 2017 at 12:11 AM
-- Server version: 10.1.20-MariaDB
-- PHP Version: 7.0.14
-- PHP Version: 7.1.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
@ -111,6 +111,7 @@ CREATE TABLE `Transactions` (
`total_sent` bigint(20) UNSIGNED NOT NULL,
`unlock_time` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`height` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`spendable` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Has 10 blocks pasted since it was indexed?',
`coinbase` tinyint(1) NOT NULL DEFAULT '0',
`payment_id` varchar(64) NOT NULL DEFAULT '',
`mixin` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
@ -203,12 +204,12 @@ ALTER TABLE `Accounts`
-- AUTO_INCREMENT for table `Inputs`
--
ALTER TABLE `Inputs`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=487;
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=117;
--
-- AUTO_INCREMENT for table `Outputs`
--
ALTER TABLE `Outputs`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=300;
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=73;
--
-- AUTO_INCREMENT for table `Payments`
--
@ -218,7 +219,7 @@ ALTER TABLE `Payments`
-- AUTO_INCREMENT for table `Transactions`
--
ALTER TABLE `Transactions`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=428;
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=115;
--
-- Constraints for dumped tables
--

@ -30,6 +30,7 @@ vector<transaction> CurrentBlockchainStatus::mempool_txs;
string CurrentBlockchainStatus::import_payment_address;
string CurrentBlockchainStatus::import_payment_viewkey;
uint64_t CurrentBlockchainStatus::import_fee {10000000000}; // 0.01 xmr
uint64_t CurrentBlockchainStatus::spendable_age {10}; // default number in monero
account_public_address CurrentBlockchainStatus::address;
secret_key CurrentBlockchainStatus::viewkey;
map<string, shared_ptr<TxSearch>> CurrentBlockchainStatus::searching_threads;
@ -108,8 +109,10 @@ CurrentBlockchainStatus::set_testnet(bool is_testnet)
bool
CurrentBlockchainStatus::init_monero_blockchain()
{
// enable basic monero log output
xmreg::enable_monero_log();
// set monero log output level
uint32_t log_level = 0;
mlog_configure("", true);
// initialize mcore and core_storage
if (!xmreg::init_blockchain(blockchain_path,
@ -122,6 +125,13 @@ CurrentBlockchainStatus::init_monero_blockchain()
return true;
}
bool
CurrentBlockchainStatus::is_tx_unlocked(uint64_t tx_blk_height)
{
return (tx_blk_height + spendable_age < get_current_blockchain_height());
}
bool
CurrentBlockchainStatus::get_block(uint64_t height, block &blk)
{
@ -146,7 +156,28 @@ CurrentBlockchainStatus::get_block_txs(const block &blk, list <transaction> &blk
return true;
}
bool
CurrentBlockchainStatus::tx_exist(const crypto::hash& tx_hash)
{
return core_storage->have_tx(tx_hash);
}
bool
CurrentBlockchainStatus::tx_exist(const string& tx_hash_str)
{
crypto::hash tx_hash;
if (hex_to_pod(tx_hash_str, tx_hash))
{
return tx_exist(tx_hash);
}
throw runtime_error("(hex_to_pod(tx_hash_str, tx_hash) failed!");
}
bool
CurrentBlockchainStatus::get_output_keys(const uint64_t& amount,
const vector<uint64_t>& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)

@ -52,6 +52,7 @@ struct CurrentBlockchainStatus
static string import_payment_address;
static string import_payment_viewkey;
static uint64_t import_fee;
static uint64_t spendable_age;
static account_public_address address;
static secret_key viewkey;
@ -87,12 +88,21 @@ struct CurrentBlockchainStatus
static bool
init_monero_blockchain();
static bool
is_tx_unlocked(uint64_t tx_blk_height);
static bool
get_block(uint64_t height, block &blk);
static bool
get_block_txs(const block &blk, list <transaction> &blk_txs);
static bool
tx_exist(const crypto::hash& tx_hash);
static bool
tx_exist(const string& tx_hash_str);
static bool
get_output_keys(const uint64_t& amount,
const vector<uint64_t>& absolute_offsets,

@ -105,6 +105,9 @@ MysqlInputs::select(const uint64_t& address_id, vector<XmrInput>& ins)
return false;
}
bool
MysqlInputs::select_for_tx(const uint64_t& address_id, vector<XmrInput>& ins)
{
@ -236,6 +239,39 @@ MysqlOutpus::select(const uint64_t& address_id, vector<XmrOutput>& outs)
return false;
}
bool
MysqlOutpus::select(const uint64_t& out_id, XmrOutput& out)
{
Query query = conn->query(XmrOutput::SELECT_STMT3);
query.parse();
try
{
vector<XmrOutput> outs;
query.storein(outs, out_id);
if (!outs.empty())
{
out = outs.at(0);
return true;
}
}
catch (mysqlpp::Exception& e)
{
MYSQL_EXCEPTION_MSG(e);
}
catch (std::exception& e)
{
MYSQL_EXCEPTION_MSG(e);
}
return false;
}
bool
MysqlOutpus::select_for_tx(const uint64_t& tx_id, vector<XmrOutput>& outs)
{
@ -417,6 +453,7 @@ MysqlTransactions::insert(const XmrTransaction& tx_data)
tx_data.unlock_time,
tx_data.height,
tx_data.coinbase,
tx_data.spendable,
tx_data.payment_id,
tx_data.mixin,
tx_data.timestamp);
@ -434,6 +471,45 @@ MysqlTransactions::insert(const XmrTransaction& tx_data)
return 0;
}
uint64_t
MysqlTransactions::mark_spendable(const uint64_t& tx_id_no)
{
Query query = conn->query(XmrTransaction::MARK_AS_SPENDABLE_STMT);
query.parse();
try
{
SimpleResult sr = query.execute(tx_id_no);
return sr.rows();
}
catch (mysqlpp::Exception& e)
{
MYSQL_EXCEPTION_MSG(e);
return 0;
}
}
uint64_t
MysqlTransactions::delete_tx(const uint64_t& tx_id_no)
{
Query query = conn->query(XmrTransaction::DELETE_STMT);
query.parse();
try
{
SimpleResult sr = query.execute(tx_id_no);
return sr.rows();
}
catch (mysqlpp::Exception& e)
{
MYSQL_EXCEPTION_MSG(e);
return 0;
}
}
bool
MysqlTransactions::exist(const uint64_t& account_id, const string& tx_hash_str, XmrTransaction& tx)
@ -790,6 +866,83 @@ MySqlAccounts::select_txs(const uint64_t& account_id, vector<XmrTransaction>& tx
}
bool
MySqlAccounts::select_txs_for_account_spendability_check(
const uint64_t& account_id,
vector<XmrTransaction>& txs)
{
vector<XmrTransaction> txs_tmp;
if (!select_txs(account_id, txs))
{
return false;
}
for (XmrTransaction& tx: txs_tmp)
{
// first we check if txs stored in db are already spendable
// it means if they are older than 10 blocks. If yes,
// we mark them as spendable, as we assumet that blocks
// older than 10 blocks are permanent, i.e, they wont get
// orphaned.
if (bool {tx.spendable} == false)
{
if (CurrentBlockchainStatus::is_tx_unlocked(tx.height))
{
// this tx was before marked as unspendable, but now
// it is spendable. Meaning, that its older than 10 blocks.
// so mark it as spendable, so that its permanet.
uint64_t no_row_updated = mark_tx_spendable(tx.id);
if (no_row_updated != 1)
{
throw runtime_error("no_row_updated != 1 "
"due to "
"xmr_accounts->mark_tx_spendable(tx.id)");
}
tx.spendable = true;
}
else
{
// tx was marked as non-spendable, i.e., younger than 10 blocks
// so we still are going to use this txs, but we need to double
// check if its still valid, i.e., it's block did not get orphaned.
// we do this by checking if txs still exists in the blockchain
// and if its in the same block as noted in the database.
if (!CurrentBlockchainStatus::tx_exist(tx.hash))
{
// tx does not exist in blockchain, but it was there before.
// probably was in orphaned block. So remove it from the
// mysql database.
uint64_t no_row_updated = delete_tx(tx.id);
if (no_row_updated != 1)
{
throw runtime_error("no_row_updated != 1 "
"due to "
"xmr_accounts->delete_tx(tx.id)");
}
continue;
}
} // else
} // if (bool {tx.spendable} == false)
txs.push_back(tx);
} //for (XmrTransaction& tx: txs_tmp)
return true;
}
bool
MySqlAccounts::select_txs_with_inputs_and_outputs(const uint64_t& account_id,
vector<XmrTransactionWithOutsAndIns>& txs)
@ -804,6 +957,12 @@ MySqlAccounts::select_outputs(const uint64_t& account_id, vector<XmrOutput>& out
return mysql_out->select(account_id, outs);
}
bool
MySqlAccounts::select_output_with_id(const uint64_t& out_id, XmrOutput& out)
{
return mysql_out->select(out_id, out);
}
bool
MySqlAccounts::select_outputs_for_tx(const uint64_t& tx_id, vector<XmrOutput>& outs)
{
@ -822,6 +981,14 @@ MySqlAccounts::select_inputs_for_tx(const uint64_t& tx_id, vector<XmrTransaction
return mysql_tx_inout->select_for_tx(tx_id, ins);
}
bool
MySqlAccounts::select_inputs_for_tx(const uint64_t& tx_id, vector<XmrInput>& ins)
{
return mysql_in->select_for_tx(tx_id, ins);
}
bool
MySqlAccounts::select_inputs_for_out(const uint64_t& output_id, vector<XmrInput>& ins)
{
@ -840,6 +1007,18 @@ MySqlAccounts::tx_exists(const uint64_t& account_id, const string& tx_hash_str,
return mysql_tx->exist(account_id, tx_hash_str, tx);
}
uint64_t
MySqlAccounts::mark_tx_spendable(const uint64_t& tx_id_no)
{
return mysql_tx->mark_spendable(tx_id_no);
}
uint64_t
MySqlAccounts::delete_tx(const uint64_t& tx_id_no)
{
return mysql_tx->delete_tx(tx_id_no);
}
uint64_t
MySqlAccounts::insert_payment(const XmrPayment& payment)
{

@ -93,6 +93,8 @@ public:
bool
select_for_tx(const uint64_t& tx_id, vector<XmrOutput>& outs);
bool
select(const uint64_t& out_id, XmrOutput& out);
bool
exist(const string& output_public_key_str, XmrOutput& out);
@ -122,6 +124,12 @@ public:
uint64_t
insert(const XmrTransaction& tx_data);
uint64_t
mark_spendable(const uint64_t& tx_id_no);
uint64_t
delete_tx(const uint64_t& tx_id_no);
bool
exist(const uint64_t& account_id, const string& tx_hash_str, XmrTransaction& tx);
@ -206,10 +214,17 @@ public:
bool
select_txs(const uint64_t& account_id, vector<XmrTransaction>& txs);
bool
select_txs_for_account_spendability_check(const uint64_t& account_id,
vector<XmrTransaction>& txs);
bool
select_txs_with_inputs_and_outputs(const uint64_t& account_id,
vector<XmrTransactionWithOutsAndIns>& txs);
bool
select_output_with_id(const uint64_t& out_id, XmrOutput& out);
bool
select_outputs(const uint64_t& account_id, vector<XmrOutput>& outs);
@ -222,14 +237,24 @@ public:
bool
select_inputs_for_tx(const uint64_t& tx_id, vector<XmrTransactionWithOutsAndIns>& ins);
bool
select_inputs_for_tx(const uint64_t& tx_id, vector<XmrInput>& ins);
bool
select_inputs_for_out(const uint64_t& output_id, vector<XmrInput>& ins);
bool
output_exists(const string& output_public_key_str, XmrOutput& out);
bool
tx_exists(const uint64_t& account_id, const string& tx_hash_str, XmrTransaction& tx);
uint64_t
mark_tx_spendable(const uint64_t& tx_id_no);
uint64_t
delete_tx(const uint64_t& tx_id_no);
uint64_t
insert_payment(const XmrPayment& payment);

@ -134,6 +134,10 @@ TxSearch::search()
searched_blk_no, pod_to_hex(get_block_hash(blk)));
}
// flag indicating whether the txs in the given block are spendable.
// this is true when block number is more than 10 blocks from current
// blockchain height.
bool is_spendable = CurrentBlockchainStatus::is_tx_unlocked(searched_blk_no);
DateTime blk_timestamp_mysql_format
= XmrTransaction::timestamp_to_DateTime(blk.timestamp);
@ -252,8 +256,6 @@ TxSearch::search()
if (mine_output)
{
string out_key_str = pod_to_hex(txout_k.key);
// found an output associated with the given address and viewkey
@ -274,13 +276,10 @@ TxSearch::search()
} // for (const auto& out: outputs)
uint64_t tx_mysql_id {0};
if (!found_mine_outputs.empty())
{
// before adding this tx and its outputs to mysql
// check if it already exists. So that we dont
// do it twice.
@ -293,9 +292,8 @@ TxSearch::search()
<< " already present in mysql"
<< endl;
continue;
}
}
tx_data.hash = tx_hash_str;
tx_data.prefix_hash = tx_prefix_hash_str;
@ -306,10 +304,13 @@ TxSearch::search()
tx_data.unlock_time = 0;
tx_data.height = searched_blk_no;
tx_data.coinbase = is_coinbase_tx;
tx_data.spendable = is_spendable;
tx_data.payment_id = CurrentBlockchainStatus::get_payment_id_as_string(tx);
tx_data.mixin = get_mixin_no(tx) - 1;
tx_data.timestamp = blk_timestamp_mysql_format;
// insert tx_data into mysql's Transactions table
tx_mysql_id = xmr_accounts->insert_tx(tx_data);
@ -329,7 +330,6 @@ TxSearch::search()
}
// now add the found outputs into Outputs tables
for (auto &out_k_idx: found_mine_outputs)
{
XmrOutput out_data;
@ -403,12 +403,22 @@ TxSearch::search()
continue;
}
//cout << "in_key.k_image): " << pod_to_hex(in_key.k_image) << endl;
// for each found output public key find check if its ours or not
for (const cryptonote::output_data_t& output_data: mixin_outputs)
// mixin counter
size_t count = 0;
// for each found output public key check if its ours or not
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);
string output_public_key_str = pod_to_hex(output_data.pubkey);
//cout << " - output_public_key_str: " << output_public_key_str << endl;
// before going to the mysql, check our known outputs cash
// if the key exists. Its much faster than going to mysql
// for this.
@ -420,6 +430,7 @@ TxSearch::search()
== known_outputs_keys.end())
{
// this mixins's output is unknown.
++count;
continue;
}
@ -440,7 +451,7 @@ TxSearch::search()
in_data.tx_id = 0; // for now zero, later we set it
in_data.output_id = out.id;
in_data.key_image = pod_to_hex(in_key.k_image);
in_data.amount = in_key.amount;
in_data.amount = out.amount; // must match corresponding output's amount
in_data.timestamp = blk_timestamp_mysql_format;
inputs_found.push_back(in_data);
@ -452,6 +463,8 @@ TxSearch::search()
} // if (xmr_accounts->output_exists(output_public_key_str, out))
count++;
} // for (const cryptonote::output_data_t& output_data: outputs)
} // for (const txin_to_key& in_key: input_key_imgs)
@ -484,11 +497,12 @@ TxSearch::search()
tx_data.hash = tx_hash_str;
tx_data.prefix_hash = tx_prefix_hash_str;
tx_data.account_id = acc->id;
tx_data.total_received = 0;
tx_data.total_received = 0; // because this is spending, total_recieved is 0
tx_data.total_sent = total_sent;
tx_data.unlock_time = 0;
tx_data.unlock_time = 0; // unlock_time is not used for now, so whatever
tx_data.height = searched_blk_no;
tx_data.coinbase = is_coinbase(tx);
tx_data.coinbase = is_coinbase_tx;
tx_data.spendable = is_spendable;
tx_data.payment_id = CurrentBlockchainStatus::get_payment_id_as_string(tx);
tx_data.mixin = get_mixin_no(tx) - 1;
tx_data.timestamp = blk_timestamp_mysql_format;
@ -501,17 +515,19 @@ TxSearch::search()
//cerr << "tx_mysql_id is zero!" << endl;
//throw TxSearchException("tx_mysql_id is zero!");
}
} // if (tx_mysql_id == 0)
// save all input found into database
for (XmrInput& in_data: inputs_found)
{
in_data.tx_id = tx_mysql_id;
uint64_t in_mysql_id = xmr_accounts->insert_input(in_data);
}
} // if (!inputs_found.empty())
} // if (!inputs_found.empty())
// save all input found into database
for (XmrInput& in_data: inputs_found)
{
in_data.tx_id = tx_mysql_id;
uint64_t in_mysql_id = xmr_accounts->insert_input(in_data);
}
} // for (const transaction& tx: blk_txs)

@ -68,8 +68,8 @@ YourMoneroRequests::login(const shared_ptr<Session> session, const Bytes & body)
{
json j_request = body_to_json(body);
if (show_logs)
print_json_log("login request: ", j_request);
// if (show_logs)
// print_json_log("login request: ", j_request);
string xmr_address = j_request["address"];
@ -160,61 +160,70 @@ YourMoneroRequests::get_address_txs(const shared_ptr< Session > session, const B
xmreg::XmrAccount acc;
// select this account if its existing one
if (xmr_accounts->select(xmr_address, acc))
{
j_response["total_received"] = acc.total_received;
j_response["start_height"] = acc.start_height;
if (xmr_accounts->select(xmr_address, acc)) {
uint64_t total_received{0};
j_response["total_received"] = total_received;
j_response["start_height"] = acc.start_height;
j_response["scanned_block_height"] = acc.scanned_block_height;
j_response["blockchain_height"] = CurrentBlockchainStatus::get_current_blockchain_height();
j_response["blockchain_height"] = CurrentBlockchainStatus::get_current_blockchain_height();
vector<XmrTransaction> txs;
// retrieve txs from mysql associated with the given address
if (xmr_accounts->select_txs(acc.id, txs))
{
if (!txs.empty())
if (xmr_accounts->select_txs_for_account_spendability_check(acc.id, txs)) {
json j_txs = json::array();
for (XmrTransaction tx: txs)
{
// we found some txs.
json j_tx = tx.to_json();
json j_txs = json::array();
vector<XmrInput> inputs;
for (XmrTransaction tx: txs)
if (xmr_accounts->select_inputs_for_tx(tx.id, inputs))
{
// get inputs associated with a given
// transaction, if any.
json j_tx = tx.to_json();
json j_spent_outputs = json::array();
vector<XmrTransactionWithOutsAndIns> inputs;
uint64_t total_spent {0};
if (xmr_accounts->select_inputs_for_tx(tx.id, inputs))
for (XmrInput input: inputs)
{
json j_spent_outputs = json::array();
uint64_t total_spent {0};
XmrOutput out;
for (XmrTransactionWithOutsAndIns input: inputs)
if (!xmr_accounts->select_output_with_id(input.output_id, out))
{
total_spent += input.amount;
j_spent_outputs.push_back(input.spent_output());
}
j_tx["total_sent"] = total_spent;
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}});
j_tx["spent_outputs"] = j_spent_outputs;
}
}
j_txs.push_back(j_tx);
}
j_tx["total_sent"] = total_spent;
j_response["transactions"] = j_txs;
j_tx["spent_outputs"] = j_spent_outputs;
} // if (!txs.empty())
} // if (xmr_accounts->select_inputs_for_tx(tx.id, inputs))
} // if (xmr_accounts->select_txs(acc.id, txs))
total_received += tx.total_received;
} // if (xmr_accounts->select(xmr_address, acc))
j_txs.push_back(j_tx);
} // for (XmrTransaction tx: txs)
j_response["total_received"] = total_received;
j_response["transactions"] = j_txs;
} // if (xmr_accounts->select_txs_for_account_spendability_check(acc.id, txs))
} // if (xmr_accounts->select(xmr_address, acc))
string response_body = j_response.dump();
@ -256,49 +265,62 @@ YourMoneroRequests::get_address_info(const shared_ptr< Session > session, const
// select this account if its existing one
if (xmr_accounts->select(xmr_address, acc))
{
uint64_t total_received {0};
// ping the search thread that we still need it.
// otherwise it will finish after some time.
CurrentBlockchainStatus::ping_search_thread(xmr_address);
j_response["total_received"] = acc.total_received;
j_response["total_received"] = total_received;
j_response["start_height"] = acc.start_height;
j_response["scanned_block_height"] = acc.scanned_block_height;
j_response["blockchain_height"] = CurrentBlockchainStatus::get_current_blockchain_height();
uint64_t total_sent {0};
vector<XmrTransactionWithOutsAndIns> txs;
vector<XmrTransaction> txs;
// retrieve txs from mysql associated with the given address
if (xmr_accounts->select_txs_with_inputs_and_outputs(acc.id, txs))
if (xmr_accounts->select_txs_for_account_spendability_check(acc.id, txs))
{
// we found some txs.
json j_spent_outputs = json::array();
if (!txs.empty())
for (XmrTransaction tx: txs)
{
//
json j_spent_outputs = json::array();
vector<XmrOutput> outs;
for (XmrTransactionWithOutsAndIns tx: txs)
if (xmr_accounts->select_outputs_for_tx(tx.id, outs))
{
if (tx.key_image.is_null)
for (XmrOutput &out: outs)
{
continue;
}
// check if the output, has been spend
vector<XmrInput> ins;
j_spent_outputs.push_back(tx.spent_output());
if (xmr_accounts->select_inputs_for_out(out.id, ins))
{
for (XmrInput& in: ins)
{
j_spent_outputs.push_back({
{"amount" , in.amount},
{"key_image" , in.key_image},
{"tx_pub_key" , out.tx_pub_key},
{"out_index" , out.out_index},
{"mixin" , out.mixin},
});
total_sent += in.amount;
}
}
total_sent += tx.amount;
total_received += out.amount;
}
}
}
j_response["spent_outputs"] = j_spent_outputs;
j_response["total_sent"] = total_sent;
} // if (!txs.empty())
j_response["total_received"] = total_received;
j_response["total_sent"] = total_sent;
} // if (xmr_accounts->select_txs_with_inputs_and_outputs(acc.id, txs))
j_response["spent_outputs"] = j_spent_outputs;
}
} // if (xmr_accounts->select(xmr_address, acc))

@ -15,8 +15,6 @@
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\002"
#include "net/http_base.h"
#include "net/http_server_handlers_map2.h"
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
@ -33,6 +31,8 @@
#include "ringct/rctOps.h"
#include "ringct/rctSigs.h"
#include "easylogging++.h"
#include "common/base58.h"
#include "string_coding.h"

@ -44,6 +44,7 @@ XmrTransaction::to_json() const
{"height" , height},
{"payment_id" , payment_id},
{"coinbase" , bool {coinbase}},
{"spendable" , bool {spendable}},
{"mixin" , mixin},
{"timestamp" , timestamp}
};

@ -58,7 +58,7 @@ struct XmrAccount : public Accounts
};
sql_create_12(Transactions, 1, 2,
sql_create_13(Transactions, 1, 2,
sql_bigint_unsigned, id,
sql_varchar , hash,
sql_varchar , prefix_hash,
@ -68,6 +68,7 @@ sql_create_12(Transactions, 1, 2,
sql_bigint_unsigned, unlock_time,
sql_bigint_unsigned, height,
sql_bool , coinbase,
sql_bool , spendable,
sql_varchar , payment_id,
sql_bigint_unsigned, mixin,
sql_timestamp , timestamp);
@ -88,16 +89,24 @@ struct XmrTransaction : public Transactions
SELECT * FROM `Transactions` WHERE `account_id` = (%0q) AND `hash` = (%1q)
)";
static constexpr const char* DELETE_STMT = R"(
DELETE FROM `Transactions` WHERE `id` = (%0q)
)";
static constexpr const char* INSERT_STMT = R"(
INSERT IGNORE INTO `Transactions` (`hash`, `prefix_hash` ,
`account_id`, `total_received`,
`total_sent`, `unlock_time`, `height`,
`coinbase`, `payment_id`, `mixin`,
`timestamp`)
INSERT IGNORE INTO `Transactions` (`hash`, `prefix_hash`, `account_id`,
`total_received`, `total_sent`, `unlock_time`,
`height`, `coinbase`, `spendable`,
`payment_id`, `mixin`, `timestamp`)
VALUES (%0q, %1q, %2q,
%3q, %4q, %5q,
%6q, %7q, %8q,
%9q, %10q);
%9q, %10q, %11q);
)";
static constexpr const char* MARK_AS_SPENDABLE_STMT = R"(
UPDATE `Transactions` SET `spendable` = 1, `timestamp` = CURRENT_TIMESTAMP
WHERE `id` = %0q;
)";
static constexpr const char* SUM_XMR_RECIEVED = R"(
@ -145,6 +154,10 @@ struct XmrOutput : public Outputs
SELECT * FROM `Outputs` WHERE `tx_id` = (%0q)
)";
static constexpr const char* SELECT_STMT3 = R"(
SELECT * FROM `Outputs` WHERE `id` = (%0q)
)";
static constexpr const char* EXIST_STMT = R"(
SELECT * FROM `Outputs` WHERE `out_pub_key` = (%0q)
)";

@ -159,15 +159,6 @@ get_payment_id(const transaction& tx,
crypto::hash& payment_id,
crypto::hash8& payment_id8);
inline void
enable_monero_log() {
uint32_t log_level = 0;
epee::log_space::get_set_log_detalisation_level(true, log_level);
epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
}
inline double
get_xmr(uint64_t core_amount)
{

Loading…
Cancel
Save