Esempio n. 1
0
bool transaction::read_from_disk(
    db_tx & tx_db, const point_out & previous_out, transaction_index & tx_index
    )
{
    set_null();

    if (
        tx_db.read_transaction_index(
        previous_out.get_hash(), tx_index) == false
        )
    {
        log_debug(
            "Transaction failed to read from disk, read transaction index "
            "failed, previous out = " <<
            previous_out.get_hash().to_string().substr(0, 20) << "."
        );
        
        return false;
    }
    
    if (read_from_disk(tx_index.get_transaction_position()) == false)
    {
        log_debug(
            "Transaction failed to read from disk, read from disk failed."
        );
        
        return false;
    }
    
    if (previous_out.n() >= m_transactions_out.size())
    {
        log_debug(
            "Transaction failed to read from disk, n is greater than outputs."
        );
        
        set_null();
        
        return false;
    }
    
    return true;
}
Esempio n. 2
0
bool transaction::fetch_inputs(
    db_tx & dbtx, const std::map<sha256, transaction_index> & test_pool,
    const bool & best_block, const bool & create_block,
    transaction::previous_t & inputs, bool & invalid
    )
{
    /**
     * If the transaction is invalid this will be set to true.
     */
    invalid = false;

    /**
     * Coinbase transactions have no inputs to fetch.
     */
    if (is_coin_base())
    {
        return true;
    }
    
    for (auto i = 0; i < m_transactions_in.size(); i++)
    {
        auto previous_out = m_transactions_in[i].previous_out();
        
        if (inputs.count(previous_out.get_hash()) > 0)
        {
            continue;
        }
        
        /**
         * Read the transaction index.
         */
        auto & tx_index = inputs[previous_out.get_hash()].first;
        
        bool found = true;
        
        if (
            (best_block || create_block) &&
            test_pool.count(previous_out.get_hash()) > 0
            )
        {
            /**
             * Get the transaction index from the current proposed changes.
             */
            tx_index = test_pool.find(previous_out.get_hash())->second;
        }
        else
        {
            /**
             * Read transaction index from transaction database.
             */
            found = dbtx.read_transaction_index(
                previous_out.get_hash(), tx_index
            );
        }
        
        if (found == false && (best_block || create_block))
        {
            if (create_block)
            {
                return false;
            }
            else
            {
                log_error(
                    "Transaction " << get_hash().to_string().substr(0, 10) <<
                    " previous transaction " <<
                    previous_out.get_hash().to_string().substr(0, 10) <<
                    " index entry not found."
                );

                return false;
            }
        }
        
        /**
         * Read previous transaction.
         */
        auto & tx_prev = inputs[previous_out.get_hash()].second;
        
        if (
            found == false ||
            tx_index.get_transaction_position() == transaction_position(1, 1, 1)
            )
        {
            if (
                transaction_pool::instance().exists(
                previous_out.get_hash()) == false
                )
            {
                log_debug(
                    "Transaction failed to fetch inputs, " <<
                    get_hash().to_string().substr(0, 10) <<
                    " pool previous transaction not found " <<
                    previous_out.get_hash().to_string().substr(0, 10) << "."
                );
                
                return false;
            }
            
            tx_prev = transaction_pool::instance().lookup(
                previous_out.get_hash()
            );
           
            if (found == false)
            {
                tx_index.spent().resize(tx_prev.transactions_out().size());
            }
        }
        else
        {
            /**
             * Read previous transaction from disk.
             */
            if (
                tx_prev.read_from_disk(
                tx_index.get_transaction_position()) == false
                )
            {
                log_error(
                    "Transaction " << get_hash().to_string().substr(0, 10) <<
                    " failed to read previous transaction " <<
                    previous_out.get_hash().to_string().substr(0, 10) <<
                    " from disk."
                );
                
                return false;
            }
        }
    }
    
    /**
     * Check that all previous out's n indexes are valid.
     */
    for (auto i = 0; i < m_transactions_in.size(); i++)
    {
        const auto & previous_out = m_transactions_in[i].previous_out();
        
        assert(inputs.count(previous_out.get_hash()) != 0);
        
        auto & tx_index = inputs[previous_out.get_hash()].first;
        
        auto & tx_prev = inputs[previous_out.get_hash()].second;
        
        if (
            previous_out.n() >= tx_prev.transactions_out().size() ||
            previous_out.n() >= tx_index.spent().size()
            )
        {
            /**
             * Revisit this if/when transaction replacement is implemented
             * and allows adding inputs.
             */
            invalid = true;
            
            log_error(
                "Transaction " << get_hash().to_string().substr(0, 10) <<
                " previous out n out of range " << previous_out.n() << ":" <<
                tx_prev.transactions_out().size() << ":" <<
                tx_index.spent().size() << " previous transaction " <<
                previous_out.get_hash().to_string().substr(0, 10) << "\n" <<
                tx_prev.to_string() << "."
            );
            
            return false;
        }
    }

    return true;
}
Esempio n. 3
0
bool transaction::disconnect_inputs(db_tx & tx_db)
{
    /**
     * Relinquish previous transactions' spent pointers.
     */
    if (is_coin_base() == false)
    {
        for (auto & i : m_transactions_in)
        {
            auto prev_out = i.previous_out();

            /**
             * Get previous transaction index from disk.
             */
            transaction_index txindex;
            
            if (
                tx_db.read_transaction_index(
                prev_out.get_hash(), txindex) == false
                )
            {
                log_error(
                    "Transaction disconnect_inputs failed, "
                    "read_transaction_index failed."
                );
                
                return false;
            }
            
            if (prev_out.n() >= txindex.spent().size())
            {
                log_error(
                    "Transaction disconnect_inputs failed, previous"
                    " out n is out of range"
                );
                
                return false;
            }
            
            /**
             * Mark outpoint as not spent.
             */
            txindex.spent()[prev_out.n()].set_null();

            /**
             * Write back.
             */
            if (
                tx_db.update_transaction_index(
                prev_out.get_hash(), txindex) == false
                )
            {
                log_error(
                    "Transaction disconnect_inputs failed, "
                    "update_transaction_index failed."
                );
                
                return false;
            }
        }
    }

    /**
     * Remove transaction from the index. This can fail if a duplicate of this
     * transaction was in a chain that got reorganized away. This is only
     * possible if this transaction was completely spent, so erasing it would
     * be a no-op anyway.
     */
    tx_db.erase_transaction_index(*this);

    return true;
}