history using memory_pool to lookup txs
authorgenjix <fake@lol.u>
Sun, 22 Apr 2012 15:02:32 +0000 (16:02 +0100)
committergenjix <fake@lol.u>
Sun, 22 Apr 2012 15:02:32 +0000 (16:02 +0100)
backends/libbitcoin/h1.py
backends/libbitcoin/history.cpp
backends/libbitcoin/memory_buffer.hpp

index 6a49710..c45dca9 100644 (file)
@@ -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()
 
index 4baaa84..76dc713 100644 (file)
@@ -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> info_unit_ptr;
+typedef std::vector<info_unit_ptr> info_unit_list;
 
 bool inputs_all_loaded(info_unit::string_list inputs)
 {
@@ -62,7 +66,8 @@ typedef std::shared_ptr<payment_entry> payment_entry_ptr;
 typedef std::vector<payment_entry_ptr> 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<query_history>
@@ -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_unit>();
+            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<const std::error_code&, const std::string&> 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<query_history>(*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));
 }
 
index 9d7621c..13ad7e6 100644 (file)
@@ -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_item> check_result;
-    typedef std::shared_ptr<check_result> 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: