trace input addresses for a tx
authorgenjix <fake@lol.u>
Sun, 22 Apr 2012 19:50:12 +0000 (20:50 +0100)
committergenjix <fake@lol.u>
Sun, 22 Apr 2012 19:50:12 +0000 (20:50 +0100)
backends/libbitcoin/trace_test.py [new file with mode: 0644]
backends/libbitcoin/trace_tx.cpp [new file with mode: 0644]

diff --git a/backends/libbitcoin/trace_test.py b/backends/libbitcoin/trace_test.py
new file mode 100644 (file)
index 0000000..6940962
--- /dev/null
@@ -0,0 +1,22 @@
+import bitcoin, trace_tx
+
+def blockchain_started(ec, chain):
+    print "Blockchain initialisation:", ec
+def handle_tx(ec, tx):
+    if ec:
+        print ec
+    trace_tx.trace_tx(service.internal_ptr, chain.internal_ptr, tx, finish)
+
+def finish(ec, result):
+    print ec
+    print result
+
+service = bitcoin.async_service(1)
+chain = bitcoin.bdb_blockchain(service, "/home/genjix/libbitcoin/database",
+                               blockchain_started)
+chain.fetch_transaction(
+    bitcoin.hash_digest("16e3e3bfbaa072e33e6a9be1df7a13ecde5ad46a8d4d4893dbecaf0c0aeeb842"),
+    handle_tx)
+
+raw_input()
+
diff --git a/backends/libbitcoin/trace_tx.cpp b/backends/libbitcoin/trace_tx.cpp
new file mode 100644 (file)
index 0000000..0b201e2
--- /dev/null
@@ -0,0 +1,141 @@
+#include <bitcoin/bitcoin.hpp>
+using namespace libbitcoin;
+
+#include <boost/python.hpp>
+namespace python = boost::python;
+
+#include "/home/genjix/python-bitcoin/src/primitive.h"
+
+namespace ph = std::placeholders;
+
+class tracer
+  : public std::enable_shared_from_this<tracer>,
+    private coroutine
+{
+public:
+    typedef std::vector<std::string> string_list;
+
+    typedef std::function<
+        void (const std::error_code&, const string_list&)> finish_handler;
+
+    tracer(async_service& service, blockchain_ptr chain)
+      : strand_(service.get_service()), chain_(chain)
+    {
+    }
+
+    void start(const message::transaction& tx, finish_handler handle_finish)
+    {
+        auto this_ptr = shared_from_this();
+        strand_.post(
+            [&, this_ptr, tx, handle_finish]()
+            {
+                tx_ = std::move(tx);
+                handle_finish_ = std::move(handle_finish);
+                this_ptr->run(std::error_code());
+            });
+    }
+
+    void run(const std::error_code& ec);
+
+private:
+    io_service::strand strand_;
+    blockchain_ptr chain_;
+
+    message::transaction tx_;
+    finish_handler handle_finish_;
+
+    size_t count_;
+    bool stopped_;
+    std::vector<script> output_scripts_;
+};
+
+typedef std::shared_ptr<tracer> tracer_ptr;
+
+void tracer::run(const std::error_code& ec)
+{
+    auto this_ptr = shared_from_this();
+    reenter(this)
+    {
+        yield
+        {
+            count_ = 0;
+            stopped_ = false;
+            output_scripts_.resize(tx_.inputs.size());
+            for (size_t input_idx = 0;
+                input_idx < tx_.inputs.size(); ++input_idx)
+            {
+                const auto& tx_input = tx_.inputs[input_idx];
+                auto prevout_idx = tx_input.previous_output.index;
+                chain_->fetch_transaction(
+                    tx_input.previous_output.hash,
+                    strand_.wrap([&, this_ptr, input_idx, prevout_idx]
+                        (const std::error_code& ec,
+                            const message::transaction& prev_tx)
+                        {
+                            if (stopped_)
+                                return;
+                            if (ec)
+                            {
+                                this_ptr->run(ec);
+                                return;
+                            }
+                            BITCOIN_ASSERT(
+                                prevout_idx < prev_tx.outputs.size());
+                            BITCOIN_ASSERT(
+                                input_idx < output_scripts_.size());
+                            output_scripts_[input_idx] =
+                                prev_tx.outputs[prevout_idx].output_script;
+                            ++count_;
+                            if (count_ == tx_.inputs.size())
+                                this_ptr->run(ec);
+                        }));
+            }
+        }
+        if (ec)
+        {
+            stopped_ = true;
+            handle_finish_(ec, string_list());
+            return;
+        }
+        string_list result;
+        for (const auto& outscript: output_scripts_)
+        {
+            payment_address addr;
+            if (!extract(addr, outscript))
+            {
+                stopped_ = true;
+                handle_finish_(ec, string_list());
+                return;
+            }
+            result.push_back(addr.encoded());
+        }
+        handle_finish_(std::error_code(), result);
+    }
+}
+
+void keep_trace_alive_proxy(const std::error_code& ec,
+    const tracer::string_list& input_addrs,
+    python::object handle_finish, tracer_ptr trace)
+{
+    python::list result;
+    for (const auto& addr: input_addrs)
+        result.append(addr);
+    pyfunction<const std::error_code&, python::list> f(handle_finish);
+    f(ec, result);
+}
+
+void trace_tx(async_service_ptr service, blockchain_ptr chain,
+    const message::transaction& tx, python::object handle_finish)
+{
+    auto trace = std::make_shared<tracer>(*service, chain);
+    trace->start(tx,
+        std::bind(keep_trace_alive_proxy, ph::_1, ph::_2,
+            handle_finish, trace));
+}
+
+BOOST_PYTHON_MODULE(trace_tx)
+{
+    using namespace boost::python;
+    def("trace_tx", trace_tx);
+}
+