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