bool leveldb_chain_keeper::clear_transaction_data( leveldb_transaction_batch& batch, const transaction_type& remove_tx) { const hash_digest& tx_hash = hash_transaction(remove_tx); batch.tx.Delete(slice(tx_hash)); // Remove spends // ... spends don't exist for coinbase txs. if (!is_coinbase(remove_tx)) for (uint32_t input_index = 0; input_index < remove_tx.inputs.size(); ++input_index) { const transaction_input_type& input = remove_tx.inputs[input_index]; // We could check if the spend matches the inpoint for safety. //const input_point inpoint{tx_hash, input_index}; // Recreate the key... data_chunk spent_key = create_spent_key(input.previous_output); // ... Perform the delete. batch.spend.Delete(slice(spent_key)); } // Remove addresses for (uint32_t output_index = 0; output_index < remove_tx.outputs.size(); ++output_index) { const transaction_output_type& output = remove_tx.outputs[output_index]; if (!remove_address(batch.addr, output.output_script, {tx_hash, output_index})) return false; } return true; }
OutputInputIdentification::OutputInputIdentification( const address_parse_info* _a, const secret_key* _v, const transaction* _tx) : total_received {0}, mixin_no {0} { address_info = _a; viewkey = _v; tx = _tx; tx_hash = get_transaction_hash(*tx); tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(*tx); tx_is_coinbase = is_coinbase(*tx); is_rct = (tx->version == 2); if (is_rct) { rct_type = tx->rct_signatures.type; } if (!generate_key_derivation(tx_pub_key, *viewkey, derivation)) { cerr << "Cant get derived key for: " << "\n" << "pub_tx_key: " << get_tx_pub_key_str() << " and " << "prv_view_key" << viewkey << endl; throw OutputInputIdentificationException("Cant get derived key for a tx"); } }
bool leveldb_validate_block::orphan_is_spent( const output_point& previous_output, size_t skip_tx, size_t skip_input) { // TODO factor this to look nicer for (size_t orphan_iter = 0; orphan_iter <= orphan_index_; ++orphan_iter) { const block_type& orphan_block = orphan_chain_[orphan_iter]->actual(); // Skip coinbase BITCOIN_ASSERT(orphan_block.transactions.size() >= 1); BITCOIN_ASSERT(is_coinbase(orphan_block.transactions[0])); for (size_t tx_index = 0; tx_index < orphan_block.transactions.size(); ++tx_index) { const transaction_type& orphan_tx = orphan_block.transactions[tx_index]; for (size_t input_index = 0; input_index < orphan_tx.inputs.size(); ++input_index) { const transaction_input_type& orphan_input = orphan_tx.inputs[input_index]; if (orphan_iter == orphan_index_ && tx_index == skip_tx && input_index == skip_input) { continue; } else if (orphan_input.previous_output == previous_output) return true; } } } return false; }
bool bdb_common::save_transaction(txn_guard_ptr txn, uint32_t block_depth, uint32_t tx_index, const hash_digest& tx_hash, const message::transaction& block_tx) { if (dupli_save(txn, tx_hash, block_depth, tx_index)) return true; // Actually add block protobuf::Transaction proto_tx = transaction_to_protobuf(block_tx); proto_tx.set_is_coinbase(is_coinbase(block_tx)); // Add parent block to transaction protobuf::Transaction_BlockPointer* proto_parent = proto_tx.add_parent(); proto_parent->set_depth(block_depth); proto_parent->set_index(tx_index); // Save tx to bdb std::ostringstream oss; if (!proto_tx.SerializeToOstream(&oss)) return false; readable_data_type key, value; key.set(tx_hash); value.set(oss.str()); // Checks for duplicates first if (db_txs_->put(txn->get(), key.get(), value.get(), DB_NOOVERWRITE) != 0) return false; if (is_coinbase(block_tx)) return true; for (uint32_t input_index = 0; input_index < block_tx.inputs.size(); ++input_index) { const message::transaction_input& input = block_tx.inputs[input_index]; const message::input_point inpoint{tx_hash, input_index}; if (!mark_spent_outputs(txn, input.previous_output, inpoint)) return false; } for (uint32_t output_index = 0; output_index < block_tx.outputs.size(); ++output_index) { const message::transaction_output& output = block_tx.outputs[output_index]; if (!add_address(txn, output.output_script, {tx_hash, output_index})) return false; } return true; }
bool leveldb_common::save_transaction(leveldb_transaction_batch& batch, uint32_t block_height, uint32_t tx_index, const hash_digest& tx_hash, const transaction_type& block_tx) { if (duplicate_exists(tx_hash, block_height, tx_index)) return true; data_chunk tx_data(8 + satoshi_raw_size(block_tx)); // Serialize tx. auto serial = make_serializer(tx_data.begin()); serial.write_4_bytes(block_height); serial.write_4_bytes(tx_index); // Actual tx data. auto end_iter = satoshi_save(block_tx, serial.iterator()); BITCOIN_ASSERT( std::distance(tx_data.begin(), end_iter) == 8 + satoshi_raw_size(block_tx)); // Save tx to leveldb batch.tx.Put(slice(tx_hash), slice(tx_data)); // Add inputs to spends database. // Coinbase inputs do not spend anything. if (!is_coinbase(block_tx)) for (uint32_t input_index = 0; input_index < block_tx.inputs.size(); ++input_index) { const transaction_input_type& input = block_tx.inputs[input_index]; const input_point inpoint{tx_hash, input_index}; if (!mark_spent_outputs(batch.spend, input.previous_output, inpoint)) return false; if (!add_debit(batch.debit, input, {tx_hash, input_index}, block_height)) return false; } // Save address -> output mappings. for (uint32_t output_index = 0; output_index < block_tx.outputs.size(); ++output_index) { const transaction_output_type& output = block_tx.outputs[output_index]; if (!add_credit(batch.credit, output, {tx_hash, output_index}, block_height)) return false; } return true; }
bool leveldb_common::save_transaction(leveldb_transaction_batch& batch, uint32_t block_height, uint32_t tx_index, const hash_digest& tx_hash, const transaction_type& block_tx) { if (is_special_duplicate(block_height, tx_index)) return true; data_chunk tx_data(8 + satoshi_raw_size(block_tx)); // Serialize tx. auto serial = make_serializer(tx_data.begin()); serial.write_4_bytes(block_height); serial.write_4_bytes(tx_index); // Actual tx data. auto end_iter = satoshi_save(block_tx, serial.iterator()); BITCOIN_ASSERT( tx_data.begin() + 8 + satoshi_raw_size(block_tx) == end_iter); // Save tx to leveldb batch.tx.Put(slice(tx_hash), slice(tx_data)); // Add inputs to spends database. // Coinbase inputs do not spend anything. if (!is_coinbase(block_tx)) for (uint32_t input_index = 0; input_index < block_tx.inputs.size(); ++input_index) { const transaction_input_type& input = block_tx.inputs[input_index]; const input_point inpoint{tx_hash, input_index}; if (!mark_spent_outputs(batch.spend, input.previous_output, inpoint)) return false; if (!add_debit(batch.debit, input, {tx_hash, input_index}, block_height)) return false; } // A stack of size 1. Keep the stealth_data from // one iteration to the next. data_chunk stealth_data_store; auto unload_stealth_store = [&]() { return std::move(stealth_data_store); }; // Save address -> output mappings. for (uint32_t output_index = 0; output_index < block_tx.outputs.size(); ++output_index) { const transaction_output_type& output = block_tx.outputs[output_index]; // If a stealth output then skip processing. if (process_stealth_output_info(output, stealth_data_store)) continue; data_chunk stealth_data = unload_stealth_store(); // Try to extract an address. payment_address address; if (!extract(address, output.script)) continue; // Process this output. if (!stealth_data.empty()) add_stealth_info(stealth_data, address, tx_hash, *db_stealth_); if (!add_credit(batch.credit, address, output.value, {tx_hash, output_index}, block_height)) return false; } return true; }
tuple<string, string, string> CurrentBlockchainStatus::construct_output_rct_field( const uint64_t global_amount_index, const uint64_t out_amount) { transaction random_output_tx; uint64_t output_idx_in_tx; // we got random outputs, but now we need to get rct data of those // outputs, because by default frontend created ringct txs. if (!CurrentBlockchainStatus::get_tx_with_output( global_amount_index, out_amount, random_output_tx, output_idx_in_tx)) { cerr << "cant get random output transaction" << endl; return make_tuple(string {}, string {}, string {}); } //cout << pod_to_hex(out.out_key) << endl; //cout << pod_to_hex(get_transaction_hash(random_output_tx)) << endl; //cout << output_idx_in_tx << endl; // placeholder variable for ringct outputs info // that we need to save in database string rtc_outpk; string rtc_mask(64, '0'); string rtc_amount(64, '0'); if (random_output_tx.version > 1 && !is_coinbase(random_output_tx)) { rtc_outpk = pod_to_hex(random_output_tx.rct_signatures.outPk[output_idx_in_tx].mask); rtc_mask = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask); rtc_amount = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].amount); } else { // for non ringct txs, we need to take it rct amount commitment // and sent to the frontend. the mask is zero mask for those, // as frontend will produce identy mask autmatically for non-ringct outputs output_data_t od = get_output_key(out_amount, global_amount_index); rtc_outpk = pod_to_hex(od.commitment); if (is_coinbase(random_output_tx)) // commenting this out. think its not needed. { // as this function provides keys for mixin outputs // not the ones we actually spend. // ringct coinbase txs are special. they have identity mask. // as suggested by this code: // https://github.com/monero-project/monero/blob/eacf2124b6822d088199179b18d4587404408e0f/src/wallet/wallet2.cpp#L893 // https://github.com/monero-project/monero/blob/master/src/blockchain_db/blockchain_db.cpp#L100 // rtc_mask = pod_to_hex(rct::identity()); } } return make_tuple(rtc_outpk, rtc_mask, rtc_amount); };
bool CurrentBlockchainStatus::search_if_payment_made( const string& payment_id_str, const uint64_t& desired_amount, string& tx_hash_with_payment) { vector<pair<uint64_t, transaction>> mempool_transactions = get_mempool_txs(); uint64_t current_blockchain_height = current_height; vector<transaction> txs_to_check; for (auto& mtx: mempool_transactions) { txs_to_check.push_back(mtx.second); } // apend txs in last to blocks into the txs_to_check vector for (uint64_t blk_i = current_blockchain_height - 10; blk_i <= current_blockchain_height; ++blk_i) { // get block cointaining this tx block blk; if (!get_block(blk_i, blk)) { cerr << "Cant get block of height: " + to_string(blk_i) << endl; return false; } list <cryptonote::transaction> blk_txs; if (!get_block_txs(blk, blk_txs)) { cerr << "Cant get transactions in block: " << to_string(blk_i) << endl; return false; } // combine mempool txs and txs from given number of // last blocks txs_to_check.insert(txs_to_check.end(), blk_txs.begin(), blk_txs.end()); } for (transaction& tx: txs_to_check) { if (is_coinbase(tx)) { // not interested in coinbase txs continue; } string tx_payment_id_str = get_payment_id_as_string(tx); // we are interested only in txs with encrypted payments id8 // they have length of 16 characters. if (tx_payment_id_str.length() != 16) { continue; } // we have some tx with encrypted payment_id8 // need to decode it using tx public key, and our // private view key, before we can comapre it is // what we are after. crypto::hash8 encrypted_payment_id8; if (!hex_to_pod(tx_payment_id_str, encrypted_payment_id8)) { cerr << "failed parsing hex to pod for encrypted_payment_id8" << '\n'; } // decrypt the encrypted_payment_id8 public_key tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(tx); // public transaction key is combined with our viewkey // to create, so called, derived key. key_derivation derivation; if (!generate_key_derivation(tx_pub_key, import_payment_viewkey, derivation)) { cerr << "Cant get derived key for: " << "\n" << "pub_tx_key: " << tx_pub_key << " and " << "prv_view_key" << import_payment_viewkey << endl; return false; } // decrypt encrypted payment id, as used in integreated addresses crypto::hash8 decrypted_payment_id8 = encrypted_payment_id8; if (decrypted_payment_id8 != null_hash8) { if (!decrypt_payment_id(decrypted_payment_id8, tx_pub_key, import_payment_viewkey)) { cerr << "Cant decrypt decrypted_payment_id8: " << pod_to_hex(decrypted_payment_id8) << "\n"; } } string decrypted_tx_payment_id_str = pod_to_hex(decrypted_payment_id8); // check if decrypted payment id matches what we have stored // in mysql. if (payment_id_str != decrypted_tx_payment_id_str) { // check tx having specific payment id only continue; } // if everything ok with payment id, we proceed with // checking if the amount transfered is correct. // for each output, in a tx, check if it belongs // to the given account of specific address and viewkey // <public_key , amount , out idx> vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs; outputs = get_ouputs_tuple(tx); string tx_hash_str = pod_to_hex(get_transaction_hash(tx)); uint64_t total_received {0}; 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, import_payment_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; // cointbase txs have amounts in plain sight. // so use amount from ringct, only for non-coinbase txs if (!is_coinbase(tx)) { bool r; r = decode_ringct(tx.rct_signatures, tx_pub_key, import_payment_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("Cant decode ringCT!"); } amount = rct_amount; } } // if (mine_output && tx.version == 2) if (mine_output) { total_received += amount; } } cout << " - payment id check in tx: " << tx_hash_str << " found: " << total_received << endl; if (total_received >= desired_amount) { // the payment has been made. tx_hash_with_payment = tx_hash_str; return true; } } return false; }