void block_database::store(const block_type& block) { const uint32_t height = index_.count(); const auto number_txs = block.transactions.size(); const uint32_t number_txs32 = static_cast<uint32_t>(number_txs); // Write block data. const auto write = [&](uint8_t* data) { satoshi_save(block.header, data); auto serial = make_serializer(data + 80); serial.write_4_bytes(height); serial.write_4_bytes(number_txs32); for (const auto& tx: block.transactions) { const auto tx_hash = hash_transaction(tx); serial.write_hash(tx_hash); } }; const auto key = hash_block_header(block.header); const auto value_size = 80 + 4 + 4 + number_txs * hash_size; const auto position = map_.store(key, write, value_size); // Write height -> position mapping. write_position(position); }
void bdb_blockchain::do_store(const message::block& stored_block, store_block_handler handle_store) { block_detail_ptr stored_detail = std::make_shared<block_detail>(stored_block); int depth = chain_->find_index(hash_block_header(stored_block)); if (depth != -1) { handle_store(error::duplicate, block_info{block_status::confirmed, depth}); return; } if (!orphans_->add(stored_detail)) { handle_store(error::duplicate, block_info{block_status::orphan, 0}); } organize_->start(); handle_store(stored_detail->errc(), stored_detail->info()); // Every N blocks, we flush database static size_t flush_counter = 0; if (++flush_counter == 2000) { env_->txn_checkpoint(0, 0, 0); flush_counter = 0; } }
int get_block_hash(Db*, const Dbt*, const Dbt* data, Dbt* second_key) { std::stringstream ss(std::string( reinterpret_cast<const char*>(data->get_data()), data->get_size())); protobuf::Block proto_block; proto_block.ParseFromIstream(&ss); message::block serial_block = protobuf_to_block_header(proto_block); second_hash = hash_block_header(serial_block); second_key->set_data(second_hash.data()); second_key->set_size(second_hash.size()); return 0; }
bool leveldb_common::save_block( uint32_t height, const block_type& serial_block) { leveldb_transaction_batch batch; // Write block header + tx hashes data_chunk raw_block_data( 80 + 4 + serial_block.transactions.size() * hash_digest_size); // Downcast to base header type so serializer selects that. auto header_end = satoshi_save( serial_block.header, raw_block_data.begin()); BITCOIN_ASSERT(std::distance(raw_block_data.begin(), header_end) == 80); auto serial_hashes = make_serializer(header_end); // Write the number of transactions... serial_hashes.write_4_bytes(serial_block.transactions.size()); // ... And now the tx themselves. for (uint32_t tx_index = 0; tx_index < serial_block.transactions.size(); ++tx_index) { const transaction_type& block_tx = serial_block.transactions[tx_index]; const hash_digest& tx_hash = hash_transaction(block_tx); if (!save_transaction(batch, height, tx_index, tx_hash, block_tx)) { log_fatal(LOG_BLOCKCHAIN) << "Could not save transaction"; return false; } serial_hashes.write_hash(tx_hash); } BITCOIN_ASSERT(serial_hashes.iterator() == raw_block_data.begin() + 80 + 4 + serial_block.transactions.size() * hash_digest_size); data_chunk raw_height = uncast_type(height); hash_digest block_hash = hash_block_header(serial_block.header); // Write block header batch.block.Put(slice(raw_height), slice(raw_block_data)); batch.block_hash.Put(slice_block_hash(block_hash), slice(raw_height)); // Execute batches. db_.write(batch); // Sync stealth database. db_stealth_->sync(height); return true; }
bool leveldb_chain_keeper::end_slice(size_t slice_begin_index, block_detail_list& sliced_blocks) { leveldb::WriteBatch blk_batch, blk_hash_batch; leveldb_transaction_batch tx_batch; leveldb_iterator it(db_blocks_->NewIterator(leveldb::ReadOptions())); data_chunk raw_depth = uncast_type(slice_begin_index); for (it->Seek(slice(raw_depth)); it->Valid(); it->Next()) { std::stringstream ss; ss.str(it->value().ToString()); protobuf::Block proto_block; proto_block.ParseFromIstream(&ss); // Convert protobuf block header into actual block block_type sliced_block; if (!reconstruct_block(common_, proto_block, sliced_block)) return false; // Add to list of sliced blocks block_detail_ptr sliced_detail = std::make_shared<block_detail>(sliced_block); sliced_blocks.push_back(sliced_detail); // Make sure to delete hash secondary index too. hash_digest block_hash = hash_block_header(sliced_block); // Delete block header... blk_batch.Delete(it->key()); // And it's secondary index. blk_hash_batch.Delete(slice_block_hash(block_hash)); // Remove txs + spends + addresses too for (const transaction_type& block_tx: sliced_block.transactions) if (!clear_transaction_data(tx_batch, block_tx)) return false; } leveldb::WriteOptions options; // Execute batches. db_blocks_->Write(options, &blk_batch); db_blocks_hash_->Write(options, &blk_hash_batch); db_txs_->Write(options, &tx_batch.tx_batch); db_spends_->Write(options, &tx_batch.spends_batch); db_address_->Write(options, &tx_batch.address_batch); return true; }
void block_database::store(const block_type& block) { const size_t height = index_.size(); // Write block data. const hash_digest key = hash_block_header(block.header); const size_t number_txs = block.transactions.size(); const size_t value_size = 80 + 4 + 4 + number_txs * hash_size; auto write = [&](uint8_t* data) { satoshi_save(block.header, data); auto serial = make_serializer(data + 80); serial.write_4_bytes(height); serial.write_4_bytes(number_txs); for (const transaction_type& tx: block.transactions) { const hash_digest tx_hash = hash_transaction(tx); serial.write_hash(tx_hash); } }; const position_type position = map_.store(key, value_size, write); // Write height -> position mapping. write_position(position); }