Esempio n. 1
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)

}
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;
}