From c66a7f1feef268d9f94280894360ea02ffecc0e9 Mon Sep 17 00:00:00 2001 From: genjix Date: Sun, 22 Apr 2012 16:02:32 +0100 Subject: [PATCH] history using memory_pool to lookup txs --- backends/libbitcoin/h1.py | 9 ++- backends/libbitcoin/history.cpp | 175 ++++++++++++++++++++++++++++++--- backends/libbitcoin/memory_buffer.hpp | 37 ++++++- 3 files changed, 203 insertions(+), 18 deletions(-) diff --git a/backends/libbitcoin/h1.py b/backends/libbitcoin/h1.py index 6a49710..c45dca9 100644 --- a/backends/libbitcoin/h1.py +++ b/backends/libbitcoin/h1.py @@ -17,9 +17,16 @@ a = bitcoin.async_service(1) chain = bitcoin.bdb_blockchain(a, "/home/genjix/libbitcoin/database", blockchain_started) txpool = bitcoin.transaction_pool(a, chain) +txdat = bitcoin.data_chunk("0100000001d6cad920a04acd6c0609cd91fe4dafa1f3b933ac90e032c78fdc19d98785f2bb010000008b483045022043f8ce02784bd7231cb362a602920f2566c18e1877320bf17d4eabdac1019b2f022100f1fd06c57330683dff50e1b4571fb0cdab9592f36e3d7e98d8ce3f94ce3f255b01410453aa8d5ddef56731177915b7b902336109326f883be759ec9da9c8f1212c6fa3387629d06e5bf5e6bcc62ec5a70d650c3b1266bb0bcc65ca900cff5311cb958bffffffff0280969800000000001976a9146025cabdbf823949f85595f3d1c54c54cd67058b88ac602d2d1d000000001976a914c55c43631ab14f7c4fd9c5f153f6b9123ec32c8888ac00000000") +ex = bitcoin.satoshi_exporter() +tx = ex.load_transaction(txdat) +def stored(ec): + print "mbuff", ec mbuff = membuf.memory_buffer(a.internal_ptr, chain.internal_ptr, txpool.internal_ptr) -address = "132nrVeRYd99TEx3y4Lu87wjkEzkh3CoJJ" +mbuff.receive(tx, stored) +address = "1GULoCDnGjhfSWzHs6zDzBxbKt9DR7uRbt" +raw_input() history.payment_history(a, chain, txpool, mbuff, address, finish) raw_input() diff --git a/backends/libbitcoin/history.cpp b/backends/libbitcoin/history.cpp index 4baaa84..76dc713 100644 --- a/backends/libbitcoin/history.cpp +++ b/backends/libbitcoin/history.cpp @@ -25,9 +25,13 @@ struct info_unit bool is_input; // set to empty if unused data_chunk raw_output_script; + + // Convenient storage used by pool txs + message::output_point previous_output; }; typedef std::shared_ptr info_unit_ptr; +typedef std::vector info_unit_list; bool inputs_all_loaded(info_unit::string_list inputs) { @@ -62,7 +66,8 @@ typedef std::shared_ptr payment_entry_ptr; typedef std::vector statement_entry; typedef std::function< - void (const std::error_code&, const statement_entry&)> finish_handler; + void (const std::error_code&, const statement_entry&, + const info_unit_list&)> finish_handler; class query_history : public std::enable_shared_from_this @@ -71,7 +76,7 @@ public: query_history(async_service& service, blockchain_ptr chain, transaction_pool_ptr txpool, memory_buffer_ptr membuf) : strand_(service.get_service()), chain_(chain), - txpool_(txpool), stopped_(false) + txpool_(txpool), membuf_(membuf), stopped_(false) { } @@ -80,12 +85,12 @@ public: if (!address_.set_encoded(address)) { handle_finish(make_error_code(std::errc::address_not_available), - statement_entry()); + statement_entry(), info_unit_list()); return; } handle_finish_ = handle_finish; chain_->fetch_outputs(address_, - strand_.wrap(std::bind(&query_history::start_loading, + strand_.wrap(std::bind(&query_history::check_membuf, shared_from_this(), ph::_1, ph::_2))); } @@ -101,20 +106,33 @@ private: { if (ec) { - handle_finish_(ec, statement_entry()); + handle_finish_(ec, statement_entry(), info_unit_list()); stop(); } return stopped_; } + void check_membuf(const std::error_code& ec, + const message::output_point_list& outpoints) + { + if (stop_on_error(ec)) + return; + membuf_->check(outpoints, address_, + strand_.wrap(std::bind(&query_history::start_loading, + shared_from_this(), ph::_1, ph::_2, outpoints))); + } + void start_loading(const std::error_code& ec, + const memory_buffer::check_result& membuf_result, const message::output_point_list& outpoints) { if (stop_on_error(ec)) return; - else if (outpoints.empty()) + else if (outpoints.empty() && membuf_result.empty()) { - handle_finish_(std::error_code(), statement_entry()); + handle_finish_(std::error_code(), + statement_entry(), info_unit_list()); + stop(); return; } for (auto outpoint: outpoints) @@ -127,6 +145,18 @@ private: shared_from_this(), ph::_1, ph::_2, entry))); load_tx_info(outpoint, entry, false); } + for (const auto& item: membuf_result) + { + auto info = std::make_shared(); + info->tx_hash = std::move(item.tx_hash); + info->index = item.index; + info->is_input = item.is_input; + info->timestamp = item.timestamp; + membuf_result_.push_back(info); + txpool_->fetch(item.tx_hash, + strand_.wrap(std::bind(&query_history::load_pool_tx, + shared_from_this(), ph::_1, ph::_2, info))); + } } void load_spend(const std::error_code& ec, @@ -155,6 +185,33 @@ private: } } + bool find_set_value_from_prevout(info_unit_ptr info) + { + const hash_digest& prev_hash = info->previous_output.hash; + for (auto entry: statement_) + { + BITCOIN_ASSERT(entry->loaded_output); + if (entry->loaded_output->tx_hash == prev_hash) + { + info->value = entry->loaded_output->value; + return true; + } + else if (entry->input_exists && + entry->loaded_input->tx_hash == prev_hash) + { + info->value = entry->loaded_output->value; + return true; + } + } + for (auto other_info: membuf_result_) + if (other_info->tx_hash == prev_hash) + { + info->value = other_info->value; + return true; + } + return false; + } + void finish_if_done() { for (auto entry: statement_) @@ -178,7 +235,16 @@ private: // Unspent outputs have a raw_output_script field } } - handle_finish_(std::error_code(), statement_); + for (auto info: membuf_result_) + { + // Lookup prevout to set the value field + if (info->is_input) + { + bool set_prevout_value = find_set_value_from_prevout(info); + BITCOIN_ASSERT(set_prevout_value); + } + } + handle_finish_(std::error_code(), statement_, membuf_result_); stop(); } @@ -293,6 +359,59 @@ private: } } + void load_pool_tx(const std::error_code& ec, + const message::transaction& tx, info_unit_ptr info) + { + if (stop_on_error(ec)) + return; + // block_hash = mempool:5 + // inputs (load from prevtx) + // outputs (load from tx) + // raw_output_script (load from tx) + // height is always None + // value (get from finish_if_done) + load_tx(tx, info); + if (info->is_input) + { + BITCOIN_ASSERT(info->index < tx.inputs.size()); + info->previous_output = tx.inputs[info->index].previous_output; + } + else + { + const auto& our_output = tx.outputs[info->index]; + info->value = our_output.value; + info->raw_output_script = save_script(our_output.output_script); + } + // If all the inputs are loaded + if (inputs_all_loaded(info->inputs)) + { + // We are the sole input + BITCOIN_ASSERT(info->is_input); + // No more inputs left to load + // This info has finished loading + info->height = 0; + info->block_hash = null_hash; + finish_if_done(); + } + + // ********* + // fetch_input_txs + + // Load the previous_output for every input so we can get + // the output address + for (size_t input_index = 0; + input_index < tx.inputs.size(); ++input_index) + { + if (info->is_input && info->index == input_index) + continue; + const auto& prevout = tx.inputs[input_index].previous_output; + chain_->fetch_transaction(prevout.hash, + strand_.wrap(std::bind(&query_history::load_input_pool_tx, + shared_from_this(), ph::_1, ph::_2, prevout.index, + info, input_index))); + } + } + void load_input_tx(const message::transaction& tx, size_t output_index, info_unit_ptr info, size_t input_index) { @@ -328,13 +447,32 @@ private: finish_if_done(); } + void load_input_pool_tx(const std::error_code& ec, + const message::transaction& tx, size_t output_index, + info_unit_ptr info, size_t input_index) + { + if (stop_on_error(ec)) + return; + load_input_tx(tx, output_index, info, input_index); + if (inputs_all_loaded(info->inputs)) + { + // No more inputs left to load + // This info has finished loading + info->height = 0; + info->block_hash = null_hash; + } + finish_if_done(); + } + io_service::strand strand_; blockchain_ptr chain_; transaction_pool_ptr txpool_; + memory_buffer_ptr membuf_; bool stopped_; statement_entry statement_; + info_unit_list membuf_result_; payment_address address_; finish_handler handle_finish_; }; @@ -359,13 +497,21 @@ void write_info(std::string& json, info_unit_ptr info) std::stringstream ss; ss << "{" << "\"tx_hash\": \"" << pretty_hex(info->tx_hash) << "\"," - << "\"block_hash\": \"" << pretty_hex(info->block_hash) << "\"," << "\"index\": " << info->index << "," // x for received, and -x for sent amounts << "\"value\": " << (info->is_input ? "-" : "") << info->value << "," - << "\"height\": " << info->height << "," << "\"timestamp\": " << info->timestamp << "," << "\"is_input\": " << info->is_input << ","; + if (info->height == 0 && info->block_hash == null_hash) + { + ss << "\"block_hash\": \"mempool\"," + << "\"height\": null,"; + } + else + { + ss << "\"block_hash\": \"" << pretty_hex(info->block_hash) << "\"," + << "\"height\": " << info->height << ","; + } write_xputs_strings(ss, "inputs", info->inputs); ss << ","; write_xputs_strings(ss, "outputs", info->outputs); @@ -378,7 +524,7 @@ void write_info(std::string& json, info_unit_ptr info) } void keep_query_alive_proxy(const std::error_code& ec, - const statement_entry& statement, + const statement_entry& statement, const info_unit_list& membuf_result, python::object handle_finish, query_history_ptr history) { std::string json = "["; @@ -396,6 +542,11 @@ void keep_query_alive_proxy(const std::error_code& ec, write_info(json, entry->loaded_input); } } + for (info_unit_ptr info: membuf_result) + { + json += ","; + write_info(json, info); + } json += "]"; pyfunction f(handle_finish); f(ec, json); @@ -408,7 +559,7 @@ void payment_history(async_service_ptr service, blockchain_ptr chain, query_history_ptr history = std::make_shared(*service, chain, txpool, membuf); history->start(address, - std::bind(keep_query_alive_proxy, ph::_1, ph::_2, + std::bind(keep_query_alive_proxy, ph::_1, ph::_2, ph::_3, handle_finish, history)); } diff --git a/backends/libbitcoin/memory_buffer.hpp b/backends/libbitcoin/memory_buffer.hpp index 9d7621c..13ad7e6 100644 --- a/backends/libbitcoin/memory_buffer.hpp +++ b/backends/libbitcoin/memory_buffer.hpp @@ -53,16 +53,13 @@ public: size_t index; bool is_input; uint64_t timestamp; - // Convenient storage - message::output_point previous_output; }; typedef std::vector check_result; - typedef std::shared_ptr check_result_ptr; typedef std::function< void (const std::error_code&)> receive_handler; typedef std::function< - void (const std::error_code&, check_result_ptr)> check_handler; + void (const std::error_code&, const check_result&)> check_handler; memory_buffer(async_service_ptr service, blockchain_ptr chain, transaction_pool_ptr txpool) @@ -93,8 +90,38 @@ public: } void check(const message::output_point_list& output_points, - const std::string& address, check_handler handle_check) + const payment_address& address, check_handler handle_check) { + auto this_ptr = shared_from_this(); + strand_.post( + [&, this_ptr, output_points, address, handle_check]() + { + check_result result; + for (auto& outpoint: output_points) + { + auto it = lookup_input_.find(outpoint); + if (it != lookup_input_.end()) + { + check_item item; + item.tx_hash = it->second.hash; + item.index = it->second.index; + item.is_input = true; + item.timestamp = timestamps_[item.tx_hash]; + result.push_back(item); + } + } + auto range = lookup_address_.equal_range(address); + for (auto it = range.first; it != range.second; ++it) + { + check_item item; + item.tx_hash = it->second.hash; + item.index = it->second.index; + item.is_input = false; + item.timestamp = timestamps_[item.tx_hash]; + result.push_back(item); + } + handle_check(std::error_code(), result); + }); } private: -- 1.7.1