C++ core with working memory pool
[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         // Convenient storage
57         message::output_point previous_output;
58     };
59     typedef std::vector<check_item> check_result;
60     typedef std::shared_ptr<check_result> check_result_ptr;
61
62     typedef std::function<
63         void (const std::error_code&)> receive_handler;
64     typedef std::function<
65         void (const std::error_code&, check_result_ptr)> check_handler;
66
67     memory_buffer(async_service_ptr service, blockchain_ptr chain,
68         transaction_pool_ptr txpool)
69       : strand_(service->get_service()), chain_(chain), txpool_(txpool)
70     {
71     }
72
73     void set_handles(python::object handle_tx_stored,
74         python::object handle_tx_confirmed)
75     {
76         auto this_ptr = shared_from_this();
77         strand_.post(
78             [&, this_ptr, handle_tx_stored, handle_tx_confirmed]()
79             {
80                 handle_tx_stored_ = handle_tx_stored;
81                 handle_tx_confirmed_ = handle_tx_confirmed;
82             });
83     }
84
85     void receive(const message::transaction& tx,
86         python::object handle_receive)
87     {
88         txpool_->store(tx,
89             strand_.wrap(std::bind(&memory_buffer::confirmed,
90                 shared_from_this(), ph::_1, tx)),
91             strand_.wrap(std::bind(&memory_buffer::stored,
92                 shared_from_this(), ph::_1, tx, handle_receive)));
93     }
94
95     void check(const message::output_point_list& output_points,
96         const std::string& address, check_handler handle_check)
97     {
98     }
99
100 private:
101     void stored(const std::error_code& ec, const message::transaction& tx,
102         python::object handle_receive)
103     {
104         if (ec)
105         {
106             pyfunction<const std::error_code&> f(handle_receive);
107             f(ec);
108             return;
109         }
110         const hash_digest& tx_hash = hash_transaction(tx);
111         for (uint32_t input_index = 0;
112             input_index < tx.inputs.size(); ++input_index)
113         {
114             const auto& prevout = tx.inputs[input_index].previous_output;
115             lookup_input_[prevout] =
116                 message::input_point{tx_hash, input_index};
117         }
118         for (uint32_t output_index = 0;
119             output_index < tx.outputs.size(); ++output_index)
120         {
121             payment_address address;
122             if (extract(address, tx.outputs[output_index].output_script))
123             {
124                 lookup_address_.insert(
125                     std::make_pair(address,
126                         message::output_point{tx_hash, output_index}));
127             }
128         }
129         timestamps_[tx_hash] = time(nullptr);
130         pyfunction<const std::error_code&> f(handle_receive);
131         f(std::error_code());
132         // tx stored
133         if (!handle_tx_stored_.is_none())
134         {
135             pyfunction<const message::transaction&> g(handle_tx_stored_);
136             g(tx);
137         }
138     }
139
140     void confirmed(const std::error_code& ec, const message::transaction& tx)
141     {
142         const hash_digest& tx_hash = hash_transaction(tx);
143         if (ec)
144         {
145             std::cerr << "Problem confirming transaction "
146                 << pretty_hex(tx_hash) << " : " << ec.message() << std::endl;
147             return;
148         }
149         std::cout << "Confirmed " << pretty_hex(tx_hash) << std::endl;
150         for (uint32_t input_index = 0;
151             input_index < tx.inputs.size(); ++input_index)
152         {
153             const auto& prevout = tx.inputs[input_index].previous_output;
154             auto it = lookup_input_.find(prevout);
155             BITCOIN_ASSERT(it != lookup_input_.end());
156             BITCOIN_ASSERT((it->second ==
157                 message::input_point{tx_hash, input_index}));
158             lookup_input_.erase(it);
159         }
160         for (uint32_t output_index = 0;
161             output_index < tx.outputs.size(); ++output_index)
162         {
163             message::output_point outpoint{tx_hash, output_index};
164             payment_address address;
165             if (extract(address, tx.outputs[output_index].output_script))
166             {
167                 auto range = lookup_address_.equal_range(address);
168                 auto it = range.first;
169                 for (; it != range.second; ++it)
170                 {
171                     if (it->second == outpoint)
172                     {
173                         lookup_address_.erase(it);
174                         break;
175                     }
176                 }
177                 BITCOIN_ASSERT(it != range.second);
178             }
179         }
180         auto time_it = timestamps_.find(tx_hash);
181         BITCOIN_ASSERT(time_it != timestamps_.end());
182         timestamps_.erase(time_it);
183         // tx_confirmed
184         if (!handle_tx_stored_.is_none())
185         {
186             pyfunction<const message::transaction&> f(handle_tx_confirmed_);
187             f(tx);
188         }
189     }
190
191     io_service::strand strand_;
192     blockchain_ptr chain_;
193     transaction_pool_ptr txpool_;
194
195     std::map<message::output_point,
196         message::input_point, outpoint_less_cmp> lookup_input_;
197     std::multimap<payment_address,
198         message::output_point, address_less_cmp> lookup_address_;
199     std::map<hash_digest, uint64_t> timestamps_;
200
201     python::object handle_tx_stored_, handle_tx_confirmed_;
202 };
203
204 typedef std::shared_ptr<memory_buffer> memory_buffer_ptr;
205