/**
     * 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 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 KeyDerivation& derivation, size_t keyIndex) {
  PublicKey pk;
  derive_public_key(derivation, keyIndex, acc.address.spendPublicKey, pk);
  return pk == out_key.key;
}
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;
}
bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex) {
  Crypto::PublicKey pk;
  derive_public_key(derivation, keyIndex, spendPublicKey, pk);
  return pk == outKey;
}
Ejemplo n.º 6
0
void
OutputInputIdentification::identify_outputs()
{
    //          <public_key  , amount  , out idx>
    vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs;

    outputs = get_ouputs_tuple(*tx);


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


        //cout  << "Chekcing output: "  << pod_to_hex(txout_k.key) << " "
        //      << "mine_output: " << mine_output << endl;


        // placeholder variable for ringct outputs info
        // that we need to save in database
        string rtc_outpk;
        string rtc_mask;
        string rtc_amount;

        // 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 value
            // for ringct, except coinbase, it will be 0
            uint64_t rct_amount_val = amount;

            // cointbase txs have amounts in plain sight.
            // so use amount from ringct, only for non-coinbase txs
            if (!tx_is_coinbase)
            {
                bool r;

                // for ringct non-coinbase txs, these values are provided with txs.
                // coinbase ringctx dont have this information. we will provide
                // them only when needed, in get_unspent_outputs. So go there
                // to see how we deal with ringct coinbase txs when we spent them
                // go to CurrentBlockchainStatus::construct_output_rct_field
                // to see how we deal with coinbase ringct that are used as mixins
                rtc_outpk  = pod_to_hex(tx->rct_signatures.outPk[output_idx_in_tx].mask);
                rtc_mask   = pod_to_hex(tx->rct_signatures.ecdhInfo[output_idx_in_tx].mask);
                rtc_amount = pod_to_hex(tx->rct_signatures.ecdhInfo[output_idx_in_tx].amount);

                rct::key mask =  tx->rct_signatures.ecdhInfo[output_idx_in_tx].mask;

                r = decode_ringct(tx->rct_signatures,
                                  tx_pub_key,
                                  *viewkey,
                                  output_idx_in_tx,
                                  mask,
                                  rct_amount_val);

                if (!r)
                {
                    cerr << "Cant decode ringCT!" << endl;
                    throw OutputInputIdentificationException("Cant decode ringCT!");
                }

                amount = rct_amount_val;
            }

        } // if (mine_output && tx.version == 2)

        if (mine_output)
        {
            string out_key_str = pod_to_hex(txout_k.key);

            // found an output associated with the given address and viewkey
            string msg = fmt::format("tx_hash:  {:s}, output_pub_key: {:s}\n",
                                     get_tx_hash_str(),
                                     out_key_str);

            cout << msg << endl;


            total_received += amount;

            identified_outputs.emplace_back(
                    output_info{
                            txout_k.key, amount, output_idx_in_tx,
                            rtc_outpk, rtc_mask, rtc_amount
                    });

        } //  if (mine_output)

    } // for (const auto& out: outputs)

}
Ejemplo n.º 7
0
bool WalletManagerImpl::checkPayment(const std::string &address_text, const std::string &txid_text, const std::string &txkey_text, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const
{
  error = "";
  cryptonote::blobdata txid_data;
  if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data))
  {
    error = tr("failed to parse txid");
    return false;
  }
  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());

  if (txkey_text.size() < 64 || txkey_text.size() % 64)
  {
    error = tr("failed to parse tx key");
    return false;
  }
  crypto::secret_key tx_key;
  cryptonote::blobdata tx_key_data;
  if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data))
  {
    error = tr("failed to parse tx key");
    return false;
  }
  tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());

  bool testnet = address_text[0] != '4';
  cryptonote::account_public_address address;
  bool has_payment_id;
  crypto::hash8 payment_id;
  if(!cryptonote::get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_text))
  {
    error = tr("failed to parse address");
    return false;
  }

  cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
  cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
  epee::net_utils::http::http_simple_client http_client;
  if (!epee::net_utils::invoke_http_json_remote_command2(daemon_address + "/gettransactions", req, res, http_client) ||
      (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
  {
    error = tr("failed to get transaction from daemon");
    return false;
  }
  cryptonote::blobdata tx_data;
  bool ok;
  if (res.txs.size() == 1)
    ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
  else
    ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
  if (!ok)
  {
    error = tr("failed to parse transaction from daemon");
    return false;
  }
  crypto::hash tx_hash, tx_prefix_hash;
  cryptonote::transaction tx;
  if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
  {
    error = tr("failed to validate transaction from daemon");
    return false;
  }
  if (tx_hash != txid)
  {
    error = tr("failed to get the right transaction from daemon");
    return false;
  }

  crypto::key_derivation derivation;
  if (!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation))
  {
    error = tr("failed to generate key derivation from supplied parameters");
    return false;
  }

  received = 0;
  try {
    for (size_t n = 0; n < tx.vout.size(); ++n)
    {
      if (typeid(cryptonote::txout_to_key) != tx.vout[n].target.type())
        continue;
      const cryptonote::txout_to_key tx_out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
      crypto::public_key pubkey;
      derive_public_key(derivation, n, address.m_spend_public_key, pubkey);
      if (pubkey == tx_out_to_key.key)
      {
        uint64_t amount;
        if (tx.version == 1)
        {
          amount = tx.vout[n].amount;
        }
        else
        {
          try
          {
            rct::key Ctmp;
            //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
            crypto::key_derivation derivation;
            bool r = crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation);
            if (!r)
            {
              LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
              amount = 0;
            }
            else
            {
              crypto::secret_key scalar1;
              crypto::derivation_to_scalar(derivation, n, scalar1);
              rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
              rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
              rct::key C = tx.rct_signatures.outPk[n].mask;
              rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
              if (rct::equalKeys(C, Ctmp))
                amount = rct::h2d(ecdh_info.amount);
              else
                amount = 0;
            }
          }
          catch (...) { amount = 0; }
        }
        received += amount;
      }
    }
  }
  catch(const std::exception &e)
  {
    LOG_ERROR("error: " << e.what());
    error = std::string(tr("error: ")) + e.what();
    return false;
  }

  if (received > 0)
  {
    LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
  }
  else
  {
    LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received nothing in txid") << " " << txid);
  }
  if (res.txs.front().in_pool)
  {
    height = 0;
  }
  else
  {
    height = res.txs.front().block_height;
  }

  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;
    }
Ejemplo n.º 9
0
bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) {
  crypto::public_key pk;
  derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk);
  return pk == out_key.key;
}
Ejemplo n.º 10
0
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;
}