bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector<uint32_t>& out, uint64_t& amount) { AccountKeys keys; keys.address = addr; // only view secret key is used, spend key is not needed keys.viewSecretKey = viewSecretKey; Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); amount = 0; size_t keyIndex = 0; uint32_t outputIndex = 0; Crypto::KeyDerivation derivation; generate_key_derivation(txPubKey, keys.viewSecretKey, derivation); for (const TransactionOutput& o : transaction.outputs) { assert(o.target.type() == typeid(KeyOutput)); if (o.target.type() == typeid(KeyOutput)) { if (is_out_to_acc(keys, boost::get<KeyOutput>(o.target), derivation, keyIndex)) { out.push_back(outputIndex); amount += o.amount; } ++keyIndex; } ++outputIndex; } return true; }
bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, const PublicKey& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered) { money_transfered = 0; size_t keyIndex = 0; size_t outputIndex = 0; KeyDerivation derivation; generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); for (const TransactionOutput& o : tx.outputs) { assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); if (o.target.type() == typeid(KeyOutput)) { if (is_out_to_acc(acc, boost::get<KeyOutput>(o.target), derivation, keyIndex)) { outs.push_back(outputIndex); money_transfered += o.amount; } ++keyIndex; } else if (o.target.type() == typeid(MultisignatureOutput)) { keyIndex += boost::get<MultisignatureOutput>(o.target).keys.size(); } ++outputIndex; } 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 tx_extra_message::decrypt(size_t index, const Crypto::PublicKey &txkey, const Crypto::SecretKey *recepient_secret_key, std::string &message) const { size_t mlen = data.size(); if (mlen < TX_EXTRA_MESSAGE_CHECKSUM_SIZE) { return false; } const char *buf; std::unique_ptr<char[]> ptr; if (recepient_secret_key != nullptr) { ptr.reset(new char[mlen]); assert(ptr); message_key_data key_data; if (!generate_key_derivation(txkey, *recepient_secret_key, key_data.derivation)) { return false; } key_data.magic1 = 0x80; key_data.magic2 = 0; Hash h = cn_fast_hash(&key_data, sizeof(message_key_data)); uint64_t nonce = SWAP64LE(index); chacha(10, data.data(), mlen, reinterpret_cast<uint8_t *>(&h), reinterpret_cast<uint8_t *>(&nonce), ptr.get()); buf = ptr.get(); } else { buf = data.data(); } mlen -= TX_EXTRA_MESSAGE_CHECKSUM_SIZE; for (size_t i = 0; i < TX_EXTRA_MESSAGE_CHECKSUM_SIZE; i++) { if (buf[mlen + i] != 0) { return false; } } message.assign(buf, mlen); return true; }
bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered) { money_transfered = 0; size_t keyIndex = 0; size_t outputIndex = 0; crypto::key_derivation derivation; generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); for (const TransactionOutput& o : tx.vout) { assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); if (o.target.type() == typeid(TransactionOutputToKey)) { if (is_out_to_acc(acc, boost::get<TransactionOutputToKey>(o.target), derivation, keyIndex)) { outs.push_back(outputIndex); money_transfered += o.amount; } ++keyIndex; } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { keyIndex += boost::get<TransactionOutputMultisignature>(o.target).keys.size(); } ++outputIndex; } return true; }
/** * Check if given output (specified by output_index) * belongs is ours based * on our private view key and public spend key */ bool is_output_ours(const size_t& output_index, const transaction& tx, const secret_key& private_view_key, const public_key& public_spend_key) { // get transaction's public key public_key pub_tx_key = get_tx_pub_key_from_extra(tx); // check if transaction has valid public key // if no, then skip if (pub_tx_key == null_pkey) { return false; } // public transaction key is combined with our viewkey // to create, so called, derived key. key_derivation derivation; if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) { cerr << "Cant get dervied key for: " << "\n" << "pub_tx_key: " << pub_tx_key << " and " << "prv_view_key" << private_view_key << endl; return false; } // get the tx output public key // that normally would be generated for us, // if someone had sent us some xmr. public_key pubkey; derive_public_key(derivation, output_index, public_spend_key, pubkey); //cout << "\n" << tx.vout.size() << " " << output_index << endl; // get tx output public key const txout_to_key tx_out_to_key = boost::get<txout_to_key>(tx.vout[output_index].target); if (tx_out_to_key.key == pubkey) { return true; } return false; }
bool tx_extra_message::encrypt(size_t index, const std::string &message, const AccountPublicAddress *recipient, const KeyPair &txkey) { size_t mlen = message.size(); std::unique_ptr<char[]> buf(new char[mlen + TX_EXTRA_MESSAGE_CHECKSUM_SIZE]); memcpy(buf.get(), message.data(), mlen); memset(buf.get() + mlen, 0, TX_EXTRA_MESSAGE_CHECKSUM_SIZE); mlen += TX_EXTRA_MESSAGE_CHECKSUM_SIZE; if (recipient) { message_key_data key_data; if (!generate_key_derivation(recipient->spendPublicKey, txkey.secretKey, key_data.derivation)) { return false; } key_data.magic1 = 0x80; key_data.magic2 = 0; Hash h = cn_fast_hash(&key_data, sizeof(message_key_data)); uint64_t nonce = SWAP64LE(index); chacha(10, buf.get(), mlen, reinterpret_cast<uint8_t *>(&h), reinterpret_cast<uint8_t *>(&nonce), buf.get()); } data.assign(buf.get(), mlen); return true; }
bool generate_key_image_helper(const AccountKeys& ack, const PublicKey& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, KeyImage& ki) { KeyDerivation recv_derivation; bool r = generate_key_derivation(tx_public_key, ack.viewSecretKey, recv_derivation); assert(r && "key image helper: failed to generate_key_derivation"); if (!r) { return false; } r = derive_public_key(recv_derivation, real_output_index, ack.address.spendPublicKey, in_ephemeral.publicKey); assert(r && "key image helper: failed to derive_public_key"); if (!r) { return false; } derive_secret_key(recv_derivation, real_output_index, ack.spendSecretKey, in_ephemeral.secretKey); generate_key_image(in_ephemeral.publicKey, in_ephemeral.secretKey, ki); return true; }
bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const PublicKey& tx_pub_key, size_t keyIndex) { KeyDerivation derivation; generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); return is_out_to_acc(acc, out_key, derivation, keyIndex); }
bool constructTransaction( const AccountKeys& sender_account_keys, const std::vector<TransactionSourceEntry>& sources, const std::vector<TransactionDestinationEntry>& destinations, std::vector<uint8_t> extra, Transaction& tx, uint64_t unlock_time, Logging::ILogger& log) { LoggerRef logger(log, "construct_tx"); tx.inputs.clear(); tx.outputs.clear(); tx.signatures.clear(); tx.version = CURRENT_TRANSACTION_VERSION; tx.unlockTime = unlock_time; tx.extra = extra; KeyPair txkey = generateKeyPair(); addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey); struct input_generation_context_data { KeyPair in_ephemeral; }; std::vector<input_generation_context_data> in_contexts; uint64_t summary_inputs_money = 0; //fill inputs for (const TransactionSourceEntry& src_entr : sources) { if (src_entr.realOutput >= src_entr.outputs.size()) { logger(ERROR) << "real_output index (" << src_entr.realOutput << ")bigger than output_keys.size()=" << src_entr.outputs.size(); return false; } summary_inputs_money += src_entr.amount; //KeyDerivation recv_derivation; in_contexts.push_back(input_generation_context_data()); KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; KeyImage img; if (!generate_key_image_helper(sender_account_keys, src_entr.realTransactionPublicKey, src_entr.realOutputIndexInTransaction, in_ephemeral, img)) return false; //check that derived key is equal with real output key if (!(in_ephemeral.publicKey == src_entr.outputs[src_entr.realOutput].second)) { logger(ERROR) << "derived public key mismatch with output public key! " << ENDL << "derived_key:" << Common::podToHex(in_ephemeral.publicKey) << ENDL << "real output_public_key:" << Common::podToHex(src_entr.outputs[src_entr.realOutput].second); return false; } //put key image into tx input KeyInput input_to_key; input_to_key.amount = src_entr.amount; input_to_key.keyImage = img; //fill outputs array and use relative offsets for (const TransactionSourceEntry::OutputEntry& out_entry : src_entr.outputs) { input_to_key.outputIndexes.push_back(out_entry.first); } input_to_key.outputIndexes = absolute_output_offsets_to_relative(input_to_key.outputIndexes); tx.inputs.push_back(input_to_key); } // "Shuffle" outs std::vector<TransactionDestinationEntry> shuffled_dsts(destinations); std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const TransactionDestinationEntry& de1, const TransactionDestinationEntry& de2) { return de1.amount < de2.amount; }); uint64_t summary_outs_money = 0; //fill outputs size_t output_index = 0; for (const TransactionDestinationEntry& dst_entr : shuffled_dsts) { if (!(dst_entr.amount > 0)) { logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; return false; } KeyDerivation derivation; PublicKey out_eph_public_key; bool r = generate_key_derivation(dst_entr.addr.viewPublicKey, txkey.secretKey, derivation); if (!(r)) { logger(ERROR, BRIGHT_RED) << "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.viewPublicKey << ", " << txkey.secretKey << ")"; return false; } r = derive_public_key(derivation, output_index, dst_entr.addr.spendPublicKey, out_eph_public_key); if (!(r)) { logger(ERROR, BRIGHT_RED) << "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", " << dst_entr.addr.spendPublicKey << ")"; return false; } TransactionOutput out; out.amount = dst_entr.amount; KeyOutput tk; tk.key = out_eph_public_key; out.target = tk; tx.outputs.push_back(out); output_index++; summary_outs_money += dst_entr.amount; } //check money if (summary_outs_money > summary_inputs_money) { logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; return false; } //generate ring signatures Hash tx_prefix_hash; getObjectHash(*static_cast<TransactionPrefix*>(&tx), tx_prefix_hash); size_t i = 0; for (const TransactionSourceEntry& src_entr : sources) { std::vector<const PublicKey*> keys_ptrs; for (const TransactionSourceEntry::OutputEntry& o : src_entr.outputs) { keys_ptrs.push_back(&o.second); } tx.signatures.push_back(std::vector<Signature>()); std::vector<Signature>& sigs = tx.signatures.back(); sigs.resize(src_entr.outputs.size()); generate_ring_signature(tx_prefix_hash, boost::get<KeyInput>(tx.inputs[i]).keyImage, keys_ptrs, in_contexts[i].in_ephemeral.secretKey, src_entr.realOutput, sigs.data()); i++; } return true; }
/** * Get tx outputs associated with the given private view and public spend keys * * */ vector<xmreg::transfer_details> get_belonging_outputs(const block& blk, const transaction& tx, const secret_key& private_view_key, const public_key& public_spend_key, uint64_t block_height) { // vector to be returned vector<xmreg::transfer_details> our_outputs; // get transaction's public key public_key pub_tx_key = get_tx_pub_key_from_extra(tx); // check if transaction has valid public key // if no, then skip if (pub_tx_key == null_pkey) { return our_outputs; } // get the total number of outputs in a transaction. size_t output_no = tx.vout.size(); // check if the given transaction has any outputs // if no, then finish if (output_no == 0) { return our_outputs; } // public transaction key is combined with our viewkey // to create, so called, derived key. key_derivation derivation; if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) { cerr << "Cant get dervied key for: " << "\n" << "pub_tx_key: " << private_view_key << " and " << "prv_view_key" << private_view_key << endl; return our_outputs; } // each tx that we (or the address we are checking) received // contains a number of outputs. // some of them are ours, some not. so we need to go through // all of them in a given tx block, to check which outputs are ours. // sum amount of xmr sent to us // in the given transaction uint64_t money_transfered {0}; // loop through outputs in the given tx // to check which outputs our ours. we compare outputs' // public keys with the public key that would had been // generated for us if we had gotten the outputs. // not sure this is the case though, but that's my understanding. for (size_t i = 0; i < output_no; ++i) { // get the tx output public key // that normally would be generated for us, // if someone had sent us some xmr. public_key pubkey; derive_public_key(derivation, i, public_spend_key, pubkey); // get tx output public key const txout_to_key tx_out_to_key = boost::get<txout_to_key>(tx.vout[i].target); //cout << "Output no: " << i << ", " << tx_out_to_key.key; // check if the output's public key is ours if (tx_out_to_key.key == pubkey) { // if so, then add this output to the // returned vector //our_outputs.push_back(tx.vout[i]); our_outputs.push_back( xmreg::transfer_details {block_height, blk.timestamp, tx, i, false} ); } } return our_outputs; }
bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) { crypto::key_derivation derivation; generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); return is_out_to_acc(acc, out_key, derivation, keyIndex); }
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; }