// // Created by mwo on 12/12/16. // #ifndef RESTBED_XMR_TXSEARCH_H #define RESTBED_XMR_TXSEARCH_H #include "MySqlConnector.h" #include "tools.h" #include "mylmdb.h" #include "CurrentBlockchainStatus.h" #include #include #include #include #include #include #include namespace xmreg { mutex mtx; class TxSearchException: public std::runtime_error { using std::runtime_error::runtime_error; }; class TxSearch { bool continue_search {true}; // represents a row in mysql's Accounts table XmrAccount acc; // this manages all mysql queries // its better to when each thread has its own mysql connection object. // this way if one thread crashes, it want take down // connection for the entire service MySqlAccounts xmr_accounts; // address and viewkey for this search thread. account_public_address address; secret_key viewkey; public: TxSearch() {} TxSearch(XmrAccount& _acc): acc {_acc}, xmr_accounts() { bool testnet = CurrentBlockchainStatus::testnet; if (!xmreg::parse_str_address(acc.address, address, testnet)) { cerr << "Cant parse string address: " << acc.address << endl; throw TxSearchException("Cant parse string address: " + acc.address); } if (!xmreg::parse_str_secret_key(acc.viewkey, viewkey)) { cerr << "Cant parse the private key: " << acc.viewkey << endl; throw TxSearchException("Cant parse private key: " + acc.viewkey); } } void search() { // start searching from last block that we searched for // this accont uint64_t searched_blk_no = acc.scanned_block_height; if (searched_blk_no > CurrentBlockchainStatus::current_height) { throw TxSearchException("searched_blk_no > CurrentBlockchainStatus::current_height"); } while(continue_search) { if (searched_blk_no > CurrentBlockchainStatus::current_height) { fmt::print("searched_blk_no {:d} and current_height {:d}\n", searched_blk_no, CurrentBlockchainStatus::current_height); std::this_thread::sleep_for( std::chrono::seconds( CurrentBlockchainStatus::refresh_block_status_every_seconds) ); continue; } // cout << " - searching tx of: " << acc << endl; // get block cointaining this tx block blk; if (!CurrentBlockchainStatus::get_block(searched_blk_no, blk)) { cerr << "Cant get block of height: " + to_string(searched_blk_no) << endl; searched_blk_no =- 2; // just go back one block, and retry continue; } // for each tx in the given block look, get ouputs list blk_txs; if (!CurrentBlockchainStatus::get_block_txs(blk, blk_txs)) { throw TxSearchException("Cant get transactions in block: " + to_string(searched_blk_no)); } //std::lock_guard lck (mtx); fmt::print(" - searching block {:d} of hash {:s} \n", searched_blk_no, pod_to_hex(get_block_hash(blk))); for (transaction& tx: blk_txs) { crypto::hash tx_hash = get_transaction_hash(tx); // cout << pod_to_hex(tx_hash) << endl; public_key tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(tx); // vector> outputs; outputs = get_ouputs_tuple(tx); // for each output, in a tx, check if it belongs // to the given account of specific address and viewkey // public transaction key is combined with our viewkey // to create, so called, derived key. key_derivation derivation; if (!generate_key_derivation(tx_pub_key, viewkey, derivation)) { cerr << "Cant get derived key for: " << "\n" << "pub_tx_key: " << tx_pub_key << " and " << "prv_view_key" << viewkey << endl; throw TxSearchException(""); } for (auto& out: outputs) { txout_to_key txout_k = std::get<0>(out); uint64_t amount = std::get<1>(out); uint64_t output_idx_in_tx = std::get<2>(out); // get the tx output public key // that normally would be generated for us, // if someone had sent us some xmr. public_key generated_tx_pubkey; derive_public_key(derivation, output_idx_in_tx, address.m_spend_public_key, generated_tx_pubkey); // check if generated public key matches the current output's key bool mine_output = (txout_k.key == generated_tx_pubkey); // if mine output has RingCT, i.e., tx version is 2 // need to decode its amount. otherwise its zero. if (mine_output && tx.version == 2) { // initialize with regular amount uint64_t rct_amount = amount; bool r; r = decode_ringct(tx.rct_signatures, tx_pub_key, viewkey, output_idx_in_tx, tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask, rct_amount); if (!r) { cerr << "Cant decode ringCT!" << endl; throw TxSearchException(""); } // cointbase txs have amounts in plain sight. // so use amount from ringct, only for non-coinbase txs if (!is_coinbase(tx)) { rct_amount = amount; } amount = rct_amount; } // if (mine_output && tx.version == 2) if (mine_output) { // found an output associated with the given address and viewkey string msg = fmt::format("block: {:d}, tx_hash: {:s}, output_pub_key: {:s}\n", searched_blk_no, pod_to_hex(tx_hash), pod_to_hex(txout_k.key)); cout << msg << endl; } } // for (const auto& out: outputs) } // for (const transaction& tx: blk_txs) ++searched_blk_no; std::this_thread::sleep_for(std::chrono::seconds(1)); } } void stop() { cout << "something to stop the thread by setting continue_search=false" << endl; } ~TxSearch() { cout << "TxSearch destroyed" << endl; } }; } #endif //RESTBED_XMR_TXSEARCH_H