예제 #1
0
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;
}
예제 #2
0
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;
}
예제 #4
0
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;
}
예제 #5
0
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;
}
예제 #6
0
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;
}
예제 #7
0
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);
};
예제 #8
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;
}