be stricter with response typing.
[electrum-server.git] / backends / libbitcoin / memory_buffer.hpp
1 #include <bitcoin/bitcoin.hpp>
2 using namespace libbitcoin;
3
4 #include "/home/genjix/python-bitcoin/src/primitive.h"
5
6 namespace ph = std::placeholders;
7
8 int cmp(const bc::message::output_point& a,
9     const bc::message::output_point& b)
10 {
11     if (a.index < b.index)
12         return -1;
13     else if (a.index > b.index)
14         return 1;
15     // a.index == b.index
16     if (a.hash < b.hash)
17         return -1;
18     else if (a.hash > b.hash)
19         return 1;
20     return 0;
21 }
22
23 struct outpoint_less_cmp
24 {
25     bool operator()(const bc::message::output_point& a,
26         const bc::message::output_point& b)
27     {
28         return cmp(a, b) == -1;
29     }
30 };
31
32 struct address_less_cmp
33 {
34     bool operator()(const payment_address& a, const payment_address& b)
35     {
36         if (a.hash() < b.hash())
37             return true;
38         else if (a.hash() > b.hash())
39             return false;
40         if (a.version() < b.version())
41             return true;
42         return false;
43     }
44 };
45
46 class memory_buffer
47   : public std::enable_shared_from_this<memory_buffer>
48 {
49 public:
50     struct check_item
51     {
52         hash_digest tx_hash;
53         size_t index;
54         bool is_input;
55         uint64_t timestamp;
56     };
57     typedef std::vector<check_item> check_result;
58
59     typedef std::function<
60         void (const std::error_code&)> receive_handler;
61     typedef std::function<
62         void (const std::error_code&, const check_result&)> check_handler;
63
64     memory_buffer(async_service_ptr service, blockchain_ptr chain,
65         transaction_pool_ptr txpool)
66       : strand_(service->get_service()), chain_(chain), txpool_(txpool)
67     {
68     }
69
70     void set_handles(python::object handle_tx_stored,
71         python::object handle_tx_confirmed)
72     {
73         auto this_ptr = shared_from_this();
74         strand_.post(
75             [&, this_ptr, handle_tx_stored, handle_tx_confirmed]()
76             {
77                 handle_tx_stored_ = handle_tx_stored;
78                 handle_tx_confirmed_ = handle_tx_confirmed;
79             });
80     }
81
82     void receive(const message::transaction& tx,
83         python::object handle_receive)
84     {
85         txpool_->store(tx,
86             strand_.wrap(std::bind(&memory_buffer::confirmed,
87                 shared_from_this(), ph::_1, tx)),
88             strand_.wrap(std::bind(&memory_buffer::stored,
89                 shared_from_this(), ph::_1, tx, handle_receive)));
90     }
91
92     void check(const message::output_point_list& output_points,
93         const payment_address& address, check_handler handle_check)
94     {
95         auto this_ptr = shared_from_this();
96         strand_.post(
97             [&, this_ptr, output_points, address, handle_check]()
98             {
99                 check_result result;
100                 for (auto& outpoint: output_points)
101                 {
102                     auto it = lookup_input_.find(outpoint);
103                     if (it != lookup_input_.end())
104                     {
105                         check_item item;
106                         item.tx_hash = it->second.hash;
107                         item.index = it->second.index;
108                         item.is_input = true;
109                         item.timestamp = timestamps_[item.tx_hash];
110                         result.push_back(item);
111                     }
112                 }
113                 auto range = lookup_address_.equal_range(address);
114                 for (auto it = range.first; it != range.second; ++it)
115                 {
116                     check_item item;
117                     item.tx_hash = it->second.hash;
118                     item.index = it->second.index;
119                     item.is_input = false;
120                     item.timestamp = timestamps_[item.tx_hash];
121                     result.push_back(item);
122                 }
123                 handle_check(std::error_code(), result);
124             });
125     }
126
127 private:
128     void stored(const std::error_code& ec, const message::transaction& tx,
129         python::object handle_receive)
130     {
131         if (ec)
132         {
133             pyfunction<const std::error_code&> f(handle_receive);
134             f(ec);
135             return;
136         }
137         const hash_digest& tx_hash = hash_transaction(tx);
138         for (uint32_t input_index = 0;
139             input_index < tx.inputs.size(); ++input_index)
140         {
141             const auto& prevout = tx.inputs[input_index].previous_output;
142             lookup_input_[prevout] =
143                 message::input_point{tx_hash, input_index};
144         }
145         for (uint32_t output_index = 0;
146             output_index < tx.outputs.size(); ++output_index)
147         {
148             payment_address address;
149             if (extract(address, tx.outputs[output_index].output_script))
150             {
151                 lookup_address_.insert(
152                     std::make_pair(address,
153                         message::output_point{tx_hash, output_index}));
154             }
155         }
156         timestamps_[tx_hash] = time(nullptr);
157         pyfunction<const std::error_code&> f(handle_receive);
158         f(std::error_code());
159         // tx stored
160         if (!handle_tx_stored_.is_none())
161         {
162             pyfunction<const message::transaction&> g(handle_tx_stored_);
163             g(tx);
164         }
165     }
166
167     void confirmed(const std::error_code& ec, const message::transaction& tx)
168     {
169         const hash_digest& tx_hash = hash_transaction(tx);
170         if (ec)
171         {
172             std::cerr << "Problem confirming transaction "
173                 << pretty_hex(tx_hash) << " : " << ec.message() << std::endl;
174             return;
175         }
176         std::cout << "Confirmed " << pretty_hex(tx_hash) << std::endl;
177         for (uint32_t input_index = 0;
178             input_index < tx.inputs.size(); ++input_index)
179         {
180             const auto& prevout = tx.inputs[input_index].previous_output;
181             auto it = lookup_input_.find(prevout);
182             BITCOIN_ASSERT(it != lookup_input_.end());
183             BITCOIN_ASSERT((it->second ==
184                 message::input_point{tx_hash, input_index}));
185             lookup_input_.erase(it);
186         }
187         for (uint32_t output_index = 0;
188             output_index < tx.outputs.size(); ++output_index)
189         {
190             message::output_point outpoint{tx_hash, output_index};
191             payment_address address;
192             if (extract(address, tx.outputs[output_index].output_script))
193             {
194                 auto range = lookup_address_.equal_range(address);
195                 auto it = range.first;
196                 for (; it != range.second; ++it)
197                 {
198                     if (it->second == outpoint)
199                     {
200                         lookup_address_.erase(it);
201                         break;
202                     }
203                 }
204                 BITCOIN_ASSERT(it != range.second);
205             }
206         }
207         auto time_it = timestamps_.find(tx_hash);
208         BITCOIN_ASSERT(time_it != timestamps_.end());
209         timestamps_.erase(time_it);
210         // tx_confirmed
211         if (!handle_tx_stored_.is_none())
212         {
213             pyfunction<const message::transaction&> f(handle_tx_confirmed_);
214             f(tx);
215         }
216     }
217
218     io_service::strand strand_;
219     blockchain_ptr chain_;
220     transaction_pool_ptr txpool_;
221
222     std::map<message::output_point,
223         message::input_point, outpoint_less_cmp> lookup_input_;
224     std::multimap<payment_address,
225         message::output_point, address_less_cmp> lookup_address_;
226     std::map<hash_digest, uint64_t> timestamps_;
227
228     python::object handle_tx_stored_, handle_tx_confirmed_;
229 };
230
231 typedef std::shared_ptr<memory_buffer> memory_buffer_ptr;
232