trace input addresses for a tx
[electrum-server.git] / backends / libbitcoin / trace_tx.cpp
1 #include <bitcoin/bitcoin.hpp>
2 using namespace libbitcoin;
3
4 #include <boost/python.hpp>
5 namespace python = boost::python;
6
7 #include "/home/genjix/python-bitcoin/src/primitive.h"
8
9 namespace ph = std::placeholders;
10
11 class tracer
12   : public std::enable_shared_from_this<tracer>,
13     private coroutine
14 {
15 public:
16     typedef std::vector<std::string> string_list;
17
18     typedef std::function<
19         void (const std::error_code&, const string_list&)> finish_handler;
20
21     tracer(async_service& service, blockchain_ptr chain)
22       : strand_(service.get_service()), chain_(chain)
23     {
24     }
25
26     void start(const message::transaction& tx, finish_handler handle_finish)
27     {
28         auto this_ptr = shared_from_this();
29         strand_.post(
30             [&, this_ptr, tx, handle_finish]()
31             {
32                 tx_ = std::move(tx);
33                 handle_finish_ = std::move(handle_finish);
34                 this_ptr->run(std::error_code());
35             });
36     }
37
38     void run(const std::error_code& ec);
39
40 private:
41     io_service::strand strand_;
42     blockchain_ptr chain_;
43
44     message::transaction tx_;
45     finish_handler handle_finish_;
46
47     size_t count_;
48     bool stopped_;
49     std::vector<script> output_scripts_;
50 };
51
52 typedef std::shared_ptr<tracer> tracer_ptr;
53
54 void tracer::run(const std::error_code& ec)
55 {
56     auto this_ptr = shared_from_this();
57     reenter(this)
58     {
59         yield
60         {
61             count_ = 0;
62             stopped_ = false;
63             output_scripts_.resize(tx_.inputs.size());
64             for (size_t input_idx = 0;
65                 input_idx < tx_.inputs.size(); ++input_idx)
66             {
67                 const auto& tx_input = tx_.inputs[input_idx];
68                 auto prevout_idx = tx_input.previous_output.index;
69                 chain_->fetch_transaction(
70                     tx_input.previous_output.hash,
71                     strand_.wrap([&, this_ptr, input_idx, prevout_idx]
72                         (const std::error_code& ec,
73                             const message::transaction& prev_tx)
74                         {
75                             if (stopped_)
76                                 return;
77                             if (ec)
78                             {
79                                 this_ptr->run(ec);
80                                 return;
81                             }
82                             BITCOIN_ASSERT(
83                                 prevout_idx < prev_tx.outputs.size());
84                             BITCOIN_ASSERT(
85                                 input_idx < output_scripts_.size());
86                             output_scripts_[input_idx] =
87                                 prev_tx.outputs[prevout_idx].output_script;
88                             ++count_;
89                             if (count_ == tx_.inputs.size())
90                                 this_ptr->run(ec);
91                         }));
92             }
93         }
94         if (ec)
95         {
96             stopped_ = true;
97             handle_finish_(ec, string_list());
98             return;
99         }
100         string_list result;
101         for (const auto& outscript: output_scripts_)
102         {
103             payment_address addr;
104             if (!extract(addr, outscript))
105             {
106                 stopped_ = true;
107                 handle_finish_(ec, string_list());
108                 return;
109             }
110             result.push_back(addr.encoded());
111         }
112         handle_finish_(std::error_code(), result);
113     }
114 }
115
116 void keep_trace_alive_proxy(const std::error_code& ec,
117     const tracer::string_list& input_addrs,
118     python::object handle_finish, tracer_ptr trace)
119 {
120     python::list result;
121     for (const auto& addr: input_addrs)
122         result.append(addr);
123     pyfunction<const std::error_code&, python::list> f(handle_finish);
124     f(ec, result);
125 }
126
127 void trace_tx(async_service_ptr service, blockchain_ptr chain,
128     const message::transaction& tx, python::object handle_finish)
129 {
130     auto trace = std::make_shared<tracer>(*service, chain);
131     trace->start(tx,
132         std::bind(keep_trace_alive_proxy, ph::_1, ph::_2,
133             handle_finish, trace));
134 }
135
136 BOOST_PYTHON_MODULE(trace_tx)
137 {
138     using namespace boost::python;
139     def("trace_tx", trace_tx);
140 }
141