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